linera-io/linera-protocol · error · ExecutionError
UnauthorizedHttpRequest
UnauthorizedHttpRequest
Error message
ExecutionError::UnauthorizedHttpRequest(url)
What it means
The HTTP oracle (PerformHttpRequest) only permits requests whose host appears in the current committee's http_request_allow_list. UnauthorizedHttpRequest is raised in three cases: the URL parses without a host, the chain has no current committee, or the host is not in the allow-list (execution_state_actor.rs:531-541). This keeps deterministic, replayable oracle calls bounded to approved endpoints.
Source
Thrown at linera-execution/src/execution_state_actor.rs:539
.headers
.into_iter()
.map(|http::Header { name, value }| {
Ok((name.parse()?, value.try_into()?))
})
.collect::<Result<HeaderMap, ExecutionError>>()?;
let url = Url::parse(&request.url)?;
let host = url
.host_str()
.ok_or_else(|| ExecutionError::UnauthorizedHttpRequest(url.clone()))?;
let (_epoch, committee) = system
.current_committee()
.await?
.ok_or_else(|| ExecutionError::UnauthorizedHttpRequest(url.clone()))?;
let allowed_hosts = &committee.policy().http_request_allow_list;
ensure!(
allowed_hosts.contains(host),
ExecutionError::UnauthorizedHttpRequest(url)
);
let request = Client::new()
.request(request.method.into(), url)
.body(request.body)
.headers(headers);
#[cfg(not(web))]
let request = request.timeout(linera_base::time::Duration::from_millis(
committee.policy().http_request_timeout_ms,
));
let response = request.send().await?;
let mut response_size_limit =
committee.policy().maximum_http_response_bytes;
View on GitHub (pinned to 6c226ddcb3)
Solutions
- Add the request host to the committee policy's http_request_allow_list and activate the epoch or committee that includes it
- Route requests through an already allow-listed host or gateway that proxies the target API
- Validate the URL has a host before issuing the request from the contract
- Confirm system.current_committee() resolves (the chain is in an active epoch) when the request runs
Example fix
// before: committee policy http_request_allow_list: ["api.gateway.example"].into(), // contract requests https://prices.example/feed -> UnauthorizedHttpRequest // after http_request_allow_list: ["api.gateway.example", "prices.example"].into(),
Defensive patterns
Strategy: validation
Validate before calling
// Client-side: check the host against a locally mirrored allow-list before the contract call
fn host_allowed(url: &str, allow_list: &BTreeSet<String>) -> Result<bool, url::ParseError> {
let parsed = url::Url::parse(url)?;
Ok(parsed.host_str().map(|h| allow_list.contains(h)).unwrap_or(false))
}
if !host_allowed(&req_url, &committee_policy.http_request_allow_list)? {
return Err(anyhow!("host not in http_request_allow_list"));
}
submit_http_oracle_request(req_url)?; Type guard
fn is_unauthorized_http_request(err: &ExecutionError) -> bool {
matches!(err, ExecutionError::UnauthorizedHttpRequest(_))
} Try / catch
match client.perform_http_request(req).await {
Ok(resp) => resp,
Err(ref e) if is_unauthorized_http_request(e) => {
// deterministic policy failure: surface which host needs allow-listing
return Err(anyhow!("request host is not allow-listed by committee policy"));
}
Err(e) => return Err(e.into()),
} Prevention
- Keep the committee allow-list mirrored in CI and validate every oracle URL against it before deployment
- Parse and sanity-check URLs (scheme and host) in client code before they reach the contract
- Never depend on ad-hoc hosts; route through an approved gateway host
When it happens
Trigger: A contract performs an HTTP request to a URL whose host is absent from the committee policy's http_request_allow_list, or the request executes before the chain has an active committee or epoch. Also triggered by malformed URLs that yield no host component.
Common situations: Pointing the oracle at an API host never added to committee policy; local or dev networks regenerated without the host; URL scheme quirks (e.g. data: or scheme-relative URLs with no host); requesting during epoch transitions when current_committee() is None.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- Failed to download certificates and update local node to the
- failed to query chain ID from RPC endpoint
- failed to check block finality — block may not exist
- no signer found for owner ${owner}
- Invalid owner address
AI-assisted analysis of linera-io/linera-protocol@6c226ddcb3 (2026-08-22).
Data as JSON: /api/errors/2f166ad457c33958.
Report an issue: GitHub.