{"record":{"id":"bd35d570c05950eb","repo":"nautechsystems/nautilus_trader","slug":"unixnanos-is-within-jiff-s-timestamp-range","errorCode":null,"errorMessage":"UnixNanos is within Jiff's timestamp range","messagePattern":"UnixNanos is within Jiff's timestamp range","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/infrastructure/src/redis/queries.rs","lineNumber":1041,"sourceCode":"        .map_err(|e| anyhow::anyhow!(\"Failed to parse instrument ID from key '{key}': {e}\"))\n}\n\nfn is_timestamp_field(key: &str) -> bool {\n    let expire_match = key == \"expire_time_ns\";\n    let ts_match = key.starts_with(\"ts_\");\n    expire_match || ts_match\n}\n\nfn convert_timestamps(value: &mut Value) {\n    match value {\n        Value::Object(map) => {\n            for (key, v) in map {\n                if is_timestamp_field(key)\n                    && let Value::Number(n) = v\n                    && let Some(n) = n.as_u64()\n                {\n                    let dt = Timestamp::from_nanosecond(i128::from(n))\n                        .expect(\"UnixNanos is within Jiff's timestamp range\");\n                    *v = Value::String(format!(\"{dt:.9}\"));\n                }\n                convert_timestamps(v);\n            }\n        }\n        Value::Array(arr) => {\n            for item in arr {\n                convert_timestamps(item);\n            }\n        }\n        _ => {}\n    }\n}\n\nfn convert_timestamp_strings(value: &mut Value) {\n    match value {\n        Value::Object(map) => {\n            for (key, v) in map {","sourceCodeStart":1023,"sourceCodeEnd":1059,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/infrastructure/src/redis/queries.rs#L1023-L1059","documentation":"In the Redis payload serializer, `convert_timestamps` rewrites numeric Unix-nanosecond timestamp fields into Jiff `Timestamp` strings formatted with nanosecond precision. The expect() asserts that any u64 field recognized as a timestamp fits within Jiff's supported timestamp range (roughly years -9999..9999). It panics when the numeric value is too large (or absurdly small) to be a real nanosecond timestamp.","triggerScenarios":"Calling `serialize_payload` on a payload where a field matched by `is_timestamp_field` (e.g. keys containing 'timestamp', 'ts') holds a u64 that is not a valid UnixNanos — such as an ID mistakenly named like a timestamp, nanoseconds stored as milliseconds*1e6 wrongly, or a corrupted/hand-edited value exceeding i128 nanoseconds representable by Jiff (≈ year 9999).","commonSituations":"Cache payloads written by an older nautilus version with different field naming; JSON produced by external systems where 'timestamp'-like keys hold sequence numbers or hashes; unit tests feeding synthetic large u64 values into timestamp-named fields.","solutions":["Verify the stored value is actually nanoseconds since the Unix epoch; convert milliseconds/microseconds to nanoseconds before caching","Rename the offending field so `is_timestamp_field` no longer matches it, or adjust the key matcher","Clamp/skip conversion for values outside Jiff's range instead of expect() (return the original value or an error)","Read the raw value at the source (redis GET / payload dump) and fix the producer writing the bad number"],"exampleFix":"// before\nlet dt = Timestamp::from_nanosecond(i128::from(n))\n    .expect(\"UnixNanos is within Jiff's timestamp range\");\n// after\nif let Ok(dt) = Timestamp::from_nanosecond(i128::from(n)) {\n    *v = Value::String(format!(\"{dt:.9}\"));\n} else {\n    log::warn!(\"skipping non-representable timestamp value {n} for key {key}\");\n}","handlingStrategy":"validation","validationCode":"const MAX_NANOS: i128 = 253_402_300_799_999_999_999; // 9999-12-31\nfn is_valid_unix_nanos(n: u64) -> bool {\n    (i128::from(n)) <= MAX_NANOS && n / 1_000_000_000 > 0\n}","typeGuard":"fn as_unix_nanos(v: &serde_json::Value) -> Option<u64> {\n    match v { Value::Number(n) => n.as_u64().filter(|n| Timestamp::from_nanosecond(i128::from(*n)).is_ok()), _ => None }\n}","tryCatchPattern":"match Timestamp::from_nanosecond(i128::from(n)) {\n    Ok(dt) => *v = Value::String(format!(\"{dt:.9}\")),\n    Err(e) => return Err(format!(\"timestamp field '{key}' out of range: {e}\")),\n}","preventionTips":["Only name fields as timestamps when they truly hold UnixNanos","Store epoch values in nanoseconds consistently (ms/us conversions at the boundary)","Version your cache keys so payload schema changes invalidate old entries","Range-check timestamp values at ingestion"],"tags":["rust","redis","timestamp","serialization","panic"],"backgroundTag":"value-out-of-range","analyzedSha":"18893faf8b356be3320add8de2f861b0b647cf06","analyzedAt":"2026-09-08T20:49:34.690Z","contentChangedAt":"2026-09-08T20:49:34.690Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}