{"record":{"id":"14217f59d48ebf34","repo":"Mintplex-Labs/anything-llm","slug":"http-error-status-response-status","errorCode":null,"errorMessage":"HTTP error! status: ${response.status}","messagePattern":"HTTP error! status: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"server/utils/agentFlows/executors/api-call.js","lineNumber":49,"sourceCode":"      } else if (body?.trim()) {\n        throw new Error(\n          \"The request body of this API Call block is not valid JSON. Open the flow in the Agent Builder and check the body for syntax errors such as missing commas, brackets, or quotes.\"\n        );\n      }\n      requestConfig.headers[\"Content-Type\"] = \"application/json\";\n    } else if (bodyType === \"text\") {\n      requestConfig.body = String(body);\n    } else {\n      requestConfig.body = body;\n    }\n  }\n\n  try {\n    introspect(`Sending body to ${url}: ${requestConfig?.body || \"No body\"}`);\n    const response = await fetch(url, requestConfig);\n    if (!response.ok) {\n      introspect(`Request failed with status ${response.status}`);\n      throw new Error(`HTTP error! status: ${response.status}`);\n    }\n\n    introspect(`API call completed`);\n    const responseText = await response.text();\n    try {\n      // JSON bodies parse into values; anything else (plain text, HTML,\n      // empty bodies) is passed through verbatim. We intentionally avoid\n      // safeJsonParse here so non-JSON bodies are never \"repaired\" into\n      // invented JSON or reduced to an embedded JSON substring.\n      return JSON.parse(responseText);\n    } catch {\n      return responseText;\n    }\n  } catch (error) {\n    console.error(error);\n    throw new Error(`API Call failed: ${error.message}`);\n  }\n}","sourceCodeStart":31,"sourceCodeEnd":67,"githubUrl":"https://github.com/Mintplex-Labs/anything-llm/blob/f92433b4ea0598492a1e6645ea22addbb4dd1287/server/utils/agentFlows/executors/api-call.js#L31-L67","documentation":"Thrown inside executeApiCall when the fetch completes but response.ok is false — i.e. the remote server answered with a non-2xx HTTP status. Only the numeric status is included (no body or statusText), so the response payload that usually explains the failure is discarded; the outer catch immediately rewraps this into 'API Call failed: HTTP error! status: N'. The introspect log 'Request failed with status N' is emitted just before the throw.","triggerScenarios":"An Agent Flow API Call block whose URL returns 401/403 (bad or missing auth header), 404 (wrong path or uninterpolated {{variable}} in the URL), 400 (validation error), 429 (rate limit), or 5xx (remote failure). Any non-2xx from the target server triggers it.","commonSituations":"API key placeholder never filled in so the header is empty; URL built from a flow variable that resolved to undefined; hitting an endpoint that requires a different method than configured; rate-limited third-party APIs; staging endpoint returning 502 while down; JSON body sent to an endpoint expecting form data.","solutions":["Reproduce the exact request outside the flow (curl with the same method, headers, body) and read the response body the flow hides.","Check the introspect log lines ('Sending body to <url>') to see the resolved URL and body — fix any empty/uninterpolated variables.","For 401/403, correct the Authorization header or API-key header the block config produces.","For 429/5xx, retry later or add error handling upstream; the block itself has no retry."],"exampleFix":null,"handlingStrategy":"retry","validationCode":"async function apiCallPrecheck(url) {\n  const parsed = new URL(url); // throws early on malformed URLs\n  if (!/^https?:$/.test(parsed.protocol)) throw new Error(\"URL must be http(s)\");\n  return true;\n}","typeGuard":null,"tryCatchPattern":"const RETRYABLE = new Set([429, 500, 502, 503, 504]);\nasync function callWithRetry(exec, attempts = 3) {\n  for (let i = 0; i < attempts; i++) {\n    try {\n      return await exec();\n    } catch (e) {\n      const m = e.message.match(/HTTP error! status: (\\d+)/);\n      if (!m || !RETRYABLE.has(Number(m[1])) || i === attempts - 1) throw e;\n      await new Promise((r) => setTimeout(r, 2 ** i * 500));\n    }\n  }\n}","preventionTips":["Test the block's exact method/URL/headers/body with curl before enabling the flow in production.","Fill auth credentials via flow variables so headers can never ship empty.","Respect 429 Retry-After; never retry 4xx other than 429 — those are config bugs, not transient failures."],"tags":["http","fetch","api-call","agent-flows","status-code"],"backgroundTag":"http-request-failed","analyzedSha":"f92433b4ea0598492a1e6645ea22addbb4dd1287","analyzedAt":"2026-08-18T10:02:21.017Z","contentChangedAt":"2026-08-18T10:02:21.017Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}