nikivdev/code · error
API error {}: {}
Error message
API error {}: {} What it means
This error is raised by `fetch_project_sealed_env` (and sibling cloud fetches) in src/env.rs when the remote API responds with any non-2xx, non-401, non-404 status. The library embeds the HTTP status code and the raw response body into the message so the developer can see exactly what the server rejected and why.
Source
Thrown at src/env.rs:1456
let resp = client
.post(url)
.header("Authorization", format!("Bearer {}", token))
.json(&body)
.send()
.context("failed to connect to cloud")?;
if resp.status() == 404 {
return Ok(identity);
}
if resp.status() == 401 {
bail!("Unauthorized. Check your token with `f env login`.");
}
if !resp.status().is_success() {
let status = resp.status();
let body = resp.text().unwrap_or_default();
bail!("API error {}: {}", status, body);
}
Ok(identity)
}
fn fetch_project_member_sealer_ids(
project_name: &str,
self_sealer_id: &str,
api_url: &str,
token: &str,
client: &reqwest::blocking::Client,
) -> Result<Vec<String>> {
let url = Url::parse(&format!(
"{}/api/env/projects/{}/sealers",
api_url, project_name
))?;
let resp = client
.get(url)View on GitHub (pinned to a747e741ae)
Solutions
- Read the status and body in the message: 5xx/429 usually mean retry later with backoff; 403 means fix project membership/permissions
- Run `f env login` to obtain a fresh token, then retry
- Check the response body for a server-side error message pointing at the failing request field
- Verify network/proxy settings if the body looks like an HTML error page from an intermediary
Example fix
// before: no handling of 429/5xx
let identity = fetch_sealed_env(...)?;
// after: retry transient statuses
match fetch_sealed_env(...) {
Err(e) if is_retryable(&e) => retry_with_backoff(3, || fetch_sealed_env(...)),
other => other,
} Defensive patterns
Strategy: retry
Validate before calling
// Rust: cheap preflight to catch auth/permission issues before heavy work
pub fn preflight(client: &Client, token: &str) -> Result<(), Error> {
let r = client.get("/api/me").bearer_auth(token).send()?;
match r.status() {
s if s.is_success() => Ok(()),
s => Err(Error::msg(format!("preflight failed: {}", s))),
}
} Type guard
pub fn is_api_error(e: &Error) -> bool {
e.to_string().starts_with("API error ")
}
pub fn api_status(e: &Error) -> Option<u16> {
e.to_string().splitn(3, ' ').nth(2)?.split(':').next()?.parse().ok()
} Try / catch
match api_status(&err) {
Some(429) | Some(500..=599) => retry_with_backoff(3, op),
Some(403) => eprintln!("permission denied: request project access"),
_ => return Err(err),
} Prevention
- Preflight the token with a cheap authenticated call before large operations
- Only retry 429/5xx; never retry 403/401 blindly
- Check project membership/role before operations requiring permissions
- Respect Retry-After headers on 429 responses
When it happens
Trigger: A `reqwest` GET/POST to the cloud env endpoint returns a status that is not 2xx, not 401, and not 404 — e.g. 403 Forbidden (token valid but lacking project access), 429 Too Many Requests, or 500/502/503 from the server.
Common situations: Expired-but-not-revoked token hitting a project the account is no longer a member of (403); server-side outage or rate limiting (429/5xx); API version mismatch after a server upgrade returning 400 Bad Request; proxy or corporate firewall altering requests.
Related errors
- Maple MCP request failed ({}): {}
- hub returned error: {}
- LM Studio returned status {}: {}
- gitedit publish failed: HTTP {}
- Gemini API error {}: {}
AI-assisted analysis of nikivdev/code@a747e741ae (2026-09-01).
Data as JSON: /api/errors/88a231e2b5657790.
Report an issue: GitHub.