Summary
When scheduling a Class C downlink via UDP Packet Forwarder, the Gateway Server occasionally computes a concentrator_timestamp that jumps far into the future, resulting in a TOO_EARLY TxAcknowledgment from the gateway.
Environment
- Version:
ttn-lw-gateway-server 3.35.2-SNAPSHOT-58357ea7da
- Protocol: UDP (Semtech UDP Packet Forwarder)
- Frequency Plan:
EU_863_870_TTN
- Device Class: Class C
What happened
While sending multicast downlinks to a Class C device, the concentrator_timestamp in the scheduled downlink message suddenly jumped from a normal value to an abnormally large one, and the gateway responded with TOO_EARLY.
Normal downlink (immediately before the problematic one)
"scheduled": {
"frequency": "869525000",
"timestamp": 2358424571,
"concentrator_timestamp": "2358424571544"
}
TxAck result: success
Problematic downlink (next one, ~3 seconds later)
"scheduled": {
"frequency": "869525000",
"timestamp": 1297309563,
"concentrator_timestamp": "5592276859385"
}
TxAck result: TOO_EARLY
Observed pattern
All previous downlinks had concentrator_timestamp values incrementing by ~3 seconds (consistent with the downlink interval):
2289389020638
2292412083309
2295423923511
2298431708900
2301387336930
2304395826750
2307411974818
2310416532287
2313438416167
2316424247158
2319463594163
2322476410236
2325503037508
2328417827289
2331408244003
2334461982748
2337467716776
2343072132934
2344621501038
2346401874715
2349443675934
2352470826616
2355487505503
2358424571544 ← last normal
5592276859385 ← sudden jump (~54 minutes into the future)
The jump from 2358424571544 to 5592276859385 is a delta of ~3233 seconds (~54 minutes), completely inconsistent with the expected ~3 second interval.
Analysis
The 32-bit timestamp field dropped from 2358424571 to 1297309563. The Gateway Server computed a future send time that crossed the 32-bit microsecond counter boundary (2^32 = 4294967296), and when converting to the 64-bit concentrator_timestamp, it accounted for a rollover:
1297309563 + 4294967296 ≈ 5592276859 → matches the concentrator_timestamp prefix of 5592276859385
The schedule attempt shows this was a Class C downlink:
"request": {
"class": "CLASS_C",
"rx2_data_rate": { "lora": { "bandwidth": 125000, "spreading_factor": 12, "coding_rate": "4/5" } },
"rx2_frequency": "869525000",
"priority": "NORMAL",
"frequency_plan_id": "EU_863_870_TTN"
}
For Class C downlinks, the Gateway Server computes the send timestamp itself (unlike Class A where it's derived from the uplink RX window). It appears the GS used a stale or incorrect concentrator clock reference when calculating the 32-bit timestamp for this downlink, producing a value (1297309563) far in the past relative to the current concentrator counter (~2.36 billion). The rollover detection then interpreted this as a future time, resulting in the abnormally large concentrator_timestamp.
Expected behavior
The concentrator_timestamp should have been approximately 2361424571544 (previous value + ~3 seconds), and the downlink should have been transmitted successfully.
Steps to reproduce
- Set up a gateway connected via UDP Packet Forwarder
- Schedule Class C downlinks at a regular interval (~3 seconds)
- Continue for an extended period (the gateway had been connected for ~20 minutes before the issue occurred)
- Observe that eventually a downlink gets an incorrect
concentrator_timestamp and fails with TOO_EARLY
Additional context
- The gateway had been connected since
2026-04-09T11:25:09Z, and the issue occurred at 2026-04-09T11:45:20Z (~20 minutes after connection).
- The gateway's uplink
timestamp values at the time were in the ~2.3-2.4 billion range, which is past the halfway point of the 32-bit counter.
- Round trip times were stable (median ~132ms), so network instability is unlikely to be the cause.
Summary
When scheduling a Class C downlink via UDP Packet Forwarder, the Gateway Server occasionally computes a
concentrator_timestampthat jumps far into the future, resulting in aTOO_EARLYTxAcknowledgment from the gateway.Environment
ttn-lw-gateway-server 3.35.2-SNAPSHOT-58357ea7daEU_863_870_TTNWhat happened
While sending multicast downlinks to a Class C device, the
concentrator_timestampin the scheduled downlink message suddenly jumped from a normal value to an abnormally large one, and the gateway responded withTOO_EARLY.Normal downlink (immediately before the problematic one)
TxAck result: success
Problematic downlink (next one, ~3 seconds later)
TxAck result:
TOO_EARLYObserved pattern
All previous downlinks had
concentrator_timestampvalues incrementing by ~3 seconds (consistent with the downlink interval):The jump from
2358424571544to5592276859385is a delta of ~3233 seconds (~54 minutes), completely inconsistent with the expected ~3 second interval.Analysis
The 32-bit
timestampfield dropped from2358424571to1297309563. The Gateway Server computed a future send time that crossed the 32-bit microsecond counter boundary (2^32 = 4294967296), and when converting to the 64-bitconcentrator_timestamp, it accounted for a rollover:1297309563 + 4294967296 ≈ 5592276859→ matches theconcentrator_timestampprefix of5592276859385The schedule attempt shows this was a Class C downlink:
For Class C downlinks, the Gateway Server computes the send timestamp itself (unlike Class A where it's derived from the uplink RX window). It appears the GS used a stale or incorrect concentrator clock reference when calculating the 32-bit
timestampfor this downlink, producing a value (1297309563) far in the past relative to the current concentrator counter (~2.36 billion). The rollover detection then interpreted this as a future time, resulting in the abnormally largeconcentrator_timestamp.Expected behavior
The
concentrator_timestampshould have been approximately2361424571544(previous value + ~3 seconds), and the downlink should have been transmitted successfully.Steps to reproduce
concentrator_timestampand fails withTOO_EARLYAdditional context
2026-04-09T11:25:09Z, and the issue occurred at2026-04-09T11:45:20Z(~20 minutes after connection).timestampvalues at the time were in the ~2.3-2.4 billion range, which is past the halfway point of the 32-bit counter.