{"record":{"id":"369a0c38e6ff78a0","repo":"astrid-runtime/astrid","slug":"signed-channel-published-at-is-unreasonably-far-in","errorCode":null,"errorMessage":"signed channel published-at is unreasonably far in the future","messagePattern":"signed channel published-at is unreasonably far in the future","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-cli/src/commands/update_channel.rs","lineNumber":487,"sourceCode":"    ensure!(\n        pointer.channel == expected_channel.as_str(),\n        \"signed channel names '{}', expected '{}'\",\n        pointer.channel,\n        expected_channel.as_str()\n    );\n    ensure!(\n        pointer.generation > 0,\n        \"signed channel generation must be positive\"\n    );\n    let published = canonical_time(&pointer.published_at, \"published-at\")?;\n    let expires = canonical_time(&pointer.expires_at, \"expires-at\")?;\n    ensure!(expires > published, \"signed channel lifetime is invalid\");\n    if let Some(now) = now {\n        ensure!(now <= expires, \"signed channel metadata has expired\");\n        let latest_reasonable_publication = now\n            .checked_add_signed(chrono::Duration::seconds(MAX_FUTURE_SKEW_SECS))\n            .context(\"channel publication skew overflowed the clock\")?;\n        ensure!(\n            published <= latest_reasonable_publication,\n            \"signed channel published-at is unreasonably far in the future\"\n        );\n    }\n    let max_lifetime = match expected_channel {\n        UpdateChannel::Stable => chrono::Duration::days(30),\n        UpdateChannel::Dev => chrono::Duration::days(7),\n        UpdateChannel::Nightly => chrono::Duration::days(2),\n    };\n    ensure!(\n        expires.signed_duration_since(published) <= max_lifetime,\n        \"signed channel lifetime exceeds the maximum for its channel\"\n    );\n    let version = canonical_version(&pointer.release.version)?;\n    let nightly_commit = nightly_source_commit(&version);\n    match expected_channel {\n        UpdateChannel::Nightly => ensure!(\n            nightly_commit.is_some() && version.build.is_empty(),","sourceCodeStart":469,"sourceCodeEnd":505,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-cli/src/commands/update_channel.rs#L469-L505","documentation":"The pointer's `published_at` must not be more than MAX_FUTURE_SKEW_SECS ahead of the caller's current time. A publication timestamp far in the future indicates a badly generated metadata file or a client clock that is far behind, either of which would break freshness and expiry reasoning, so it is rejected.","triggerScenarios":"parse_channel or enforce_continuity sees `published_at > now + MAX_FUTURE_SKEW_SECS`; usually a publisher writing local (non-UTC) time misinterpreted as UTC, or the client's clock set months/years behind.","commonSituations":"Machine with wrong year/RTC battery dead (clock reset to 2020) making a 2026 publish look absurdly future; publisher script formatting a naive datetime without timezone; fabricated metadata.","solutions":["Sync the local clock via NTP (`timedatectl set-ntp true`) and retry","Check the publishing tool writes `published-at` as UTC RFC 3339 (e.g. `2026-09-09T12:00:00Z`)","Re-generate the channel pointer with a correct timestamp if you produced it","Verify no offset was dropped when serializing chrono DateTime to the TOML"],"exampleFix":"// before: naive local time serialized without offset\npublished-at = \"2026-09-09T12:00:00\"\n// after\npublished-at = \"2026-09-09T12:00:00Z\"","handlingStrategy":"validation","validationCode":"fn publish_time_is_reasonable(p: &ChannelPointer, now: chrono::DateTime<Utc>) -> bool {\n    p.published_at <= now + chrono::Duration::seconds(MAX_FUTURE_SKEW_SECS)\n}","typeGuard":"fn published_not_in_future(p: &ChannelPointer, now: chrono::DateTime<Utc>) -> bool {\n    p.published_at <= now + chrono::Duration::seconds(MAX_FUTURE_SKEW_SECS)\n}","tryCatchPattern":"match parse_channel(&bytes, channel, Utc::now()) {\n    Err(e) if e.to_string().contains(\"unreasonably far in the future\") => {\n        anyhow::bail!(\"local clock is likely wrong or metadata timestamp is bad; sync clock and retry\")\n    }\n    other => other?,\n}","preventionTips":["Publishers must serialize UTC RFC 3339 timestamps with explicit offset","Run NTP on both publisher and client machines","Refuse to publish if published_at is ahead of the publisher's own clock","Sanity-check timestamps in CI before signing"],"tags":["update-channel","timestamp","clock-skew"],"backgroundTag":"invalid-date-format","analyzedSha":"affd8760f44190dbdfbec23403f4c4b642c33112","analyzedAt":"2026-09-09T21:28:12.402Z","contentChangedAt":"2026-09-09T21:28:12.402Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}