astrid-runtime/astrid · error

signed channel lifetime is invalid

Error message

signed channel lifetime is invalid

What it means

The pointer's `expires_at` timestamp must be strictly after its `published_at` timestamp (both parsed by canonical_time). A non-positive lifetime means the metadata is self-inconsistent — it expired before or exactly when it was published — so it is rejected before any freshness checks run.

Solutions

  1. Ensure `expires_at` is strictly later than `published_at` (e.g. published_at + channel max lifetime)
  2. Check both fields are in the same format/timezone expected by canonical_time (RFC 3339/UTC)
  3. Re-generate the pointer with the official publishing tool rather than editing timestamps by hand
  4. If only expiry passed too early, republish the channel with a corrected expiry

Example fix

// before (channel.toml)
published-at = "2026-09-01T00:00:00Z"
expires-at = "2026-09-01T00:00:00Z"
// after
published-at = "2026-09-01T00:00:00Z"
expires-at = "2026-09-02T00:00:00Z"
Defensive patterns

Strategy: validation

Validate before calling

fn lifetime_is_positive(p: &ChannelPointer) -> bool {
    p.expires_at > p.published_at
}

Type guard

fn has_positive_lifetime(p: &ChannelPointer) -> bool {
    p.expires_at > p.published_at
}

Try / catch

match parse_channel(&bytes, channel, now) {
    Err(e) if e.to_string().contains("lifetime is invalid") => {
        anyhow::bail!("channel pointer timestamps are inconsistent (expires <= published); republish")
    }
    other => other?,
}

Prevention

When it happens

Trigger: parse_channel or enforce_continuity reads a pointer where `expires_at <= published_at`, e.g. `expires_at` copied from `published_at`, a swapped field order, or a publisher computing expiry with an off-by-zero duration.

Common situations: Hand-edited channel TOML with swapped timestamps; a publishing script using the wrong variable for expiry; timezone/UTC conversion mistakes shifting one timestamp by hours so ordering flips.

Related errors


AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09). Data as JSON: /api/errors/e57290a2d1728b82. Report an issue: GitHub.

Appendix: source

Thrown at crates/astrid-cli/src/commands/update_channel.rs:481

        pointer.schema_version == 1
            && pointer.kind == "astrid-channel"
            && pointer.product == PRODUCT
            && pointer.repository == REPOSITORY,
        "signed channel identity is invalid"
    );
    ensure!(
        pointer.channel == expected_channel.as_str(),
        "signed channel names '{}', expected '{}'",
        pointer.channel,
        expected_channel.as_str()
    );
    ensure!(
        pointer.generation > 0,
        "signed channel generation must be positive"
    );
    let published = canonical_time(&pointer.published_at, "published-at")?;
    let expires = canonical_time(&pointer.expires_at, "expires-at")?;
    ensure!(expires > published, "signed channel lifetime is invalid");
    if let Some(now) = now {
        ensure!(now <= expires, "signed channel metadata has expired");
        let latest_reasonable_publication = now
            .checked_add_signed(chrono::Duration::seconds(MAX_FUTURE_SKEW_SECS))
            .context("channel publication skew overflowed the clock")?;
        ensure!(
            published <= latest_reasonable_publication,
            "signed channel published-at is unreasonably far in the future"
        );
    }
    let max_lifetime = match expected_channel {
        UpdateChannel::Stable => chrono::Duration::days(30),
        UpdateChannel::Dev => chrono::Duration::days(7),
        UpdateChannel::Nightly => chrono::Duration::days(2),
    };
    ensure!(
        expires.signed_duration_since(published) <= max_lifetime,
        "signed channel lifetime exceeds the maximum for its channel"

View on GitHub (pinned to affd8760f4)