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.
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
- gitedit publish failed: HTTP
- hub returned error
- LM Studio returned status
- Maple MCP request failed
- device auth poll failed: HTTP
AI-assisted analysis of nikivdev/code@a747e741ae (2026-09-01).
Data as JSON: /api/errors/88a231e2b5657790.
Report an issue: GitHub.
Appendix: 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)