jdx/mise · error
invalid GitHub relay timeout policy
Error message
invalid GitHub relay timeout policy
What it means
The connected relay advertises its timeout policy in the x-mise-relay-timeout header, parsed as a Duration. adapter_client validates that the duration is non-zero and that it can be added to the current instant without overflow; a zero duration or an unparseable/overflowing value is rejected with "invalid GitHub relay timeout policy" so the client never builds a client with a useless or overflowing read timeout.
Source
Thrown at src/github_relay.rs:1026
let response = builder()
.build()?
.get("http://localhost/_session")
.timeout(Duration::from_secs(3))
.send()
.await
.map_err(|error| {
eyre::Report::new(error.without_url()).wrap_err("GitHub relay policy unavailable")
})?;
if response.status() != 204 {
bail!("GitHub relay is not connected");
}
let value = response
.headers()
.get("x-mise-relay-timeout")
.ok_or_else(|| eyre::eyre!("GitHub relay timeout policy missing"))?;
let timeout: Duration = serde_json::from_slice(value.as_bytes())?;
if timeout.is_zero() || std::time::Instant::now().checked_add(timeout).is_none() {
bail!("invalid GitHub relay timeout policy");
}
Ok((builder().read_timeout(timeout).build()?, timeout))
}
// Bound connecting and writing as well as waiting for response headers.
// The client's read timeout separately protects the streamed response.
async fn send_adapter_request(
req: reqwest::RequestBuilder,
timeout: Duration,
) -> Result<reqwest::Response> {
tokio::time::timeout(timeout, req.send())
.await
.map_err(|_| eyre::eyre!("GitHub relay request setup timed out"))?
.map_err(|error| {
eyre::Report::new(error.without_url())
.wrap_err("GitHub relay disconnected or unavailable")
})
}View on GitHub (pinned to afd2eddd3a)
Solutions
- Update the relay and client to matching versions so the x-mise-relay-timeout header is serialized consistently.
- Restart the session to get a fresh relay process emitting a valid timeout policy.
- Remove any intermediary proxy on the relay's loopback path that could strip or rewrite the custom header.
Defensive patterns
Strategy: try-catch
Try / catch
match result {
Err(e) if e.to_string().contains("invalid GitHub relay timeout policy") => {
// restart the relay/session to get a fresh, valid header
}
other => other?,
} Prevention
- Keep relay and client versions in sync so the header format matches.
- Don't put proxies or middleware on the relay's loopback path that could mangle headers.
- Treat a zero timeout in the header as a relay bug and fail fast with a restart.
When it happens
Trigger: The relay responds 204 but its x-mise-relay-timeout header is missing-valid: it's "0", absent of proper Duration serialization, corrupted, or absurdly large enough to overflow Instant::checked_add.
Common situations: Version mismatch between client and relay where the header format changed; a broken/patched relay build emitting a zero timeout; proxy middleware stripping or mangling the custom header.
Understand the failure class
Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- cannot record a hit for a missing action result
- the first agent request must be hello
- cache agent returned an incomplete blob lookup response
- cache agent returned an unexpected blob lookup response
- cache agent did not return the rustc identity
AI-assisted analysis of jdx/mise@afd2eddd3a (2026-09-09).
Data as JSON: /api/errors/8d6939ea4ff507ad.
Report an issue: GitHub.