{"record":{"id":"db099d36362658fa","repo":"sigoden/aichat","slug":"invalid-status","errorCode":null,"errorMessage":"Invalid status: {}","messagePattern":"Invalid status: (.+?)","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"src/utils/request.rs","lineNumber":85,"sourceCode":"    Ok(output)\n}\n\npub async fn fetch_with_loaders(\n    loaders: &HashMap<String, String>,\n    path: &str,\n    allow_media: bool,\n) -> Result<(String, String)> {\n    if let Some(loader_command) = loaders.get(URL_LOADER) {\n        let contents = run_loader_command(path, URL_LOADER, loader_command)?;\n        return Ok((contents, DEFAULT_EXTENSION.into()));\n    }\n    let client = match *CLIENT {\n        Ok(ref client) => client,\n        Err(ref err) => bail!(\"{err}\"),\n    };\n    let mut res = client.get(path).send().await?;\n    if !res.status().is_success() {\n        bail!(\"Invalid status: {}\", res.status());\n    }\n    let content_type = res\n        .headers()\n        .get(CONTENT_TYPE)\n        .and_then(|v| v.to_str().ok())\n        .map(|v| match v.split_once(';') {\n            Some((mime, _)) => mime.trim(),\n            None => v,\n        })\n        .map(|v| v.to_string())\n        .unwrap_or_else(|| {\n            format!(\n                \"_/{}\",\n                get_patch_extension(path).unwrap_or_else(|| DEFAULT_EXTENSION.into())\n            )\n        });\n    let mut is_media = false;\n    let extension = match content_type.as_str() {","sourceCodeStart":67,"sourceCodeEnd":103,"githubUrl":"https://github.com/sigoden/aichat/blob/82976d349ad97ac9aae0655ad631dace5e2a6385/src/utils/request.rs#L67-L103","documentation":"fetch_with_loaders performs an HTTP GET and bails with 'Invalid status: <code>' when the response status is not a success (non-2xx). This converts any HTTP error status (404, 401, 403, 429, 5xx) into a library error including the status code.","triggerScenarios":"load_url / load_documents fetching a URL that returns non-2xx: dead link (404), auth-required resource (401/403), rate limiting (429), or server errors (5xx).","commonSituations":"URL points to a private/auth-gated resource; page moved or deleted; scraping endpoints that block non-browser clients; temporary provider outage or rate limit.","solutions":["Check the status code in the message: fix auth (401/403) or correct the URL (404)","Add required headers/cookies or use an authenticated client for gated resources","Retry with backoff for 429/5xx responses","Configure a url_loader (e.g. a headless-browser loader) for pages that reject plain GETs","Verify network/proxy access to the host"],"exampleFix":"// before: fetching a gist without auth -> 404\nlet doc = load_url(\"https://gist.github.com/private/abc\").await?;\n// after: use raw authenticated URL or a loader with token\nlet doc = load_url(\"https://gist.githubusercontent.com/.../raw?token=...\").await?;","handlingStrategy":"retry","validationCode":"async function reachable(url) {\n  try {\n    const res = await fetch(url, { method: 'HEAD' });\n    return res.ok;\n  } catch { return false; }\n}","typeGuard":null,"tryCatchPattern":"try {\n  const doc = await loadUrl(url);\n} catch (e) {\n  const m = e.message.match(/Invalid status: (\\d+)/);\n  if (m) {\n    const code = +m[1];\n    if (code === 429 || code >= 500) await retryWithBackoff(() => loadUrl(url));\n    else console.error(`non-retryable status ${code} for ${url}`);\n  }\n}","preventionTips":["Check links for liveness before loading","Handle 429/5xx with exponential backoff","Use authenticated/raw URLs for gated content","Configure a url_loader for pages that block plain GETs"],"tags":["http","network","status-code","url-fetch"],"backgroundTag":"http-error-response","analyzedSha":"82976d349ad97ac9aae0655ad631dace5e2a6385","analyzedAt":"2026-09-09T18:33:06.139Z","contentChangedAt":"2026-09-09T18:33:06.139Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}