astrid-runtime/astrid · error
signed channel lifetime exceeds the maximum for its channel
Error message
signed channel lifetime exceeds the maximum for its channel
What it means
Each channel has a maximum allowed pointer lifetime: stable 30 days, dev 7 days, nightly 2 days. Even if expiry is after publication, a pointer valid longer than its channel's cap is rejected, because long-lived pointers undermine the short-expiry trust model (a compromised file would remain usable too long).
Solutions
- Set `expires_at = published_at + max_lifetime` for the channel (30d stable, 7d dev, 2d nightly) and republish
- Parameterize the publishing script's expiry per channel instead of sharing one constant
- If the pointer came from upstream and is oversized, report/fix the release automation and use a freshly generated pointer
- Shorten the lifetime and re-sign the metadata
Example fix
// before: nightly published with stable-style expiry
let expires = published + Duration::days(30);
// after
let expires = match channel {
UpdateChannel::Nightly => published + Duration::days(2),
UpdateChannel::Dev => published + Duration::days(7),
UpdateChannel::Stable => published + Duration::days(30),
}; Defensive patterns
Strategy: validation
Validate before calling
fn lifetime_within_cap(p: &ChannelPointer, channel: UpdateChannel) -> bool {
let days = match channel { UpdateChannel::Stable => 30, UpdateChannel::Dev => 7, UpdateChannel::Nightly => 2 };
p.expires_at.signed_duration_since(p.published_at) <= chrono::Duration::days(days)
} Type guard
fn lifetime_ok(p: &ChannelPointer, channel: &UpdateChannel) -> bool {
let max = match channel {
UpdateChannel::Stable => chrono::Duration::days(30),
UpdateChannel::Dev => chrono::Duration::days(7),
UpdateChannel::Nightly => chrono::Duration::days(2),
};
p.expires_at.signed_duration_since(p.published_at) <= max
} Try / catch
match parse_channel(&bytes, channel, now) {
Err(e) if e.to_string().contains("exceeds the maximum") => {
anyhow::bail!("publisher misconfigured expiry for this channel; report to feed operator")
}
other => other?,
} Prevention
- Parameterize expiry duration per channel in the publishing tool
- Never reuse stable's 30-day expiry template for dev/nightly
- Add a publisher-side assertion mirroring validate_pointer's max_lifetime table
When it happens
Trigger: parse_channel or enforce_continuity reads a pointer where `expires_at - published_at` exceeds the cap for `expected_channel`, e.g. a nightly with expires 30 days after publish.
Common situations: Publisher script reusing the stable expiry (30d) constant for nightly/dev builds; hand-edited expiry extended to 'keep updates working'; template files copied between channels without adjusting lifetimes.
Understand the failure class
Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.
Related errors
- signed channel lifetime is invalid
- must contain exactly targets
- nightly channel must point to an exact nightly prerelease
- signed channel generation must be positive
- signed channel names
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/c7c7f54d5f2d3a8c.
Report an issue: GitHub.
Appendix: source
Thrown at crates/astrid-cli/src/commands/update_channel.rs:497
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"
);
let version = canonical_version(&pointer.release.version)?;
let nightly_commit = nightly_source_commit(&version);
match expected_channel {
UpdateChannel::Nightly => ensure!(
nightly_commit.is_some() && version.build.is_empty(),
"nightly channel must point to an exact nightly prerelease"
),
UpdateChannel::Stable | UpdateChannel::Dev => ensure!(
version.pre.is_empty() && version.build.is_empty(),
"stable and dev channels must point to canonical releases"
),
}
ensure!(
pointer.release.tag == format!("v{version}"),
"signed channel release tag does not match its version"View on GitHub (pinned to affd8760f4)