{"record":{"id":"1e60fcf5ba3b3616","repo":"nautechsystems/nautilus_trader","slug":"chain-head-skew-age-and-future-drift-limits-must","errorCode":null,"errorMessage":"Chain head skew, age, and future-drift limits must be nonzero","messagePattern":"Chain head skew, age, and future-drift limits must be nonzero","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/adapters/blockchain/src/rpc/verification.rs","lineNumber":1224,"sourceCode":"        normalize_endpoint(authoritative_url)?,\n        normalize_endpoint(config.verifiers[0].http_rpc_url.expose_secret())?,\n        normalize_endpoint(config.verifiers[1].http_rpc_url.expose_secret())?,\n    ];\n    ensure_distinct(endpoints.iter().map(String::as_str), \"provider endpoints\")?;\n\n    let anchor = &config.chain_anchor;\n    anyhow::ensure!(anchor.chain_id != 0, \"Chain anchor ID must be nonzero\");\n    anyhow::ensure!(\n        !anchor.chain_name.trim().is_empty(),\n        \"Chain anchor name is required\"\n    );\n    let checkpoint_hash = B256::from_str(&anchor.checkpoint_hash)\n        .map_err(|_| anyhow::anyhow!(\"Chain checkpoint hash must contain 32 hexadecimal bytes\"))?;\n    anyhow::ensure!(\n        checkpoint_hash != B256::ZERO,\n        \"Chain checkpoint hash must be nonzero\"\n    );\n    anyhow::ensure!(\n        anchor.max_head_skew_blocks != 0\n            && anchor.max_head_age_secs != 0\n            && anchor.max_future_drift_secs != 0,\n        \"Chain head skew, age, and future-drift limits must be nonzero\"\n    );\n    anyhow::ensure!(\n        !config.manifest_version.trim().is_empty(),\n        \"Deployment manifest version is required\"\n    );\n    let manifest_digest = B256::from_str(&config.manifest_digest).map_err(|_| {\n        anyhow::anyhow!(\"Deployment manifest digest must contain 32 hexadecimal bytes\")\n    })?;\n    anyhow::ensure!(\n        manifest_digest != B256::ZERO,\n        \"Deployment manifest digest must be nonzero\"\n    );\n    let manifest = &config.deployment_manifest;\n    anyhow::ensure!(","sourceCodeStart":1206,"sourceCodeEnd":1242,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/blockchain/src/rpc/verification.rs#L1206-L1242","documentation":"The validator requires a chain anchor's freshness limits — max_head_skew_blocks, max_head_age_secs, and max_future_drift_secs — to all be nonzero. These values bound how far the observed chain head may lag, how old the head may be, and how far it may run ahead of local time. Zero would make head verification either trivially fail or be undefined, so the library rejects it up front.","triggerScenarios":"Validating a ChainAnchor where any of `max_head_skew_blocks`, `max_head_age_secs`, or `max_future_drift_secs` is 0 while the rest of the anchor (name, checkpoint hash) is already valid.","commonSituations":"Hand-written TOML/JSON configs where these numeric fields were omitted and defaulted to 0; a migration copying anchors without the new skew/drift fields; operators setting 0 expecting 'no limit' semantics.","solutions":["Populate all three fields with positive values matching the chain's block time and your freshness tolerance (e.g. skew of a few blocks, age/drift of tens of seconds).","Copy the values from a known-good anchor config for the same chain.","Do not use 0 to mean 'unlimited'; the schema requires strictly positive limits."],"exampleFix":"// before\nlet anchor = ChainAnchor { max_head_skew_blocks: 0, max_head_age_secs: 0, max_future_drift_secs: 0, ..anchor };\n// after\nlet anchor = ChainAnchor { max_head_skew_blocks: 4, max_head_age_secs: 60, max_future_drift_secs: 30, ..anchor };","handlingStrategy":"validation","validationCode":"fn limits_ok(anchor: &ChainAnchor) -> bool {\n    anchor.max_head_skew_blocks > 0\n        && anchor.max_head_age_secs > 0\n        && anchor.max_future_drift_secs > 0\n}\nassert!(limits_ok(&anchor), \"all head freshness limits must be positive\");","typeGuard":"fn has_positive_limits(a: &ChainAnchor) -> bool {\n    a.max_head_skew_blocks != 0 && a.max_head_age_secs != 0 && a.max_future_drift_secs != 0\n}","tryCatchPattern":null,"preventionTips":["Use serde defaults that error on missing fields instead of silently defaulting numeric limits to 0.","Document that 0 is invalid; pick explicit generous limits (e.g. 4 blocks, 60s, 30s) per chain.","Validate anchor configs in CI with a small Rust test that runs the same ensure! checks."],"tags":["validation","config","blockchain"],"backgroundTag":"invalid-config-value","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"}