{"record":{"id":"56bb3f06107b9c54","repo":"Stirling-Tools/Stirling-PDF","slug":"detail-ai-engine-request-failed-response-st","errorCode":null,"errorMessage":"detail ?? `AI engine request failed: ${response.status}`","messagePattern":"detail \\?\\? `AI engine request failed: (.+?)`","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"frontend/editor/src/proprietary/components/chat/ChatContext.tsx","lineNumber":615,"sourceCode":"            // non-JSON body — ignore\n          }\n          if (limitHandled) {\n            dispatch({ type: \"SET_PROGRESS\", progress: null });\n            dispatch({\n              type: \"ADD_MESSAGE\",\n              message: {\n                id: generateId(),\n                role: ChatRole.ASSISTANT,\n                content: t(\n                  \"chat.responses.usage_limit_reached\",\n                  \"You've reached your usage limit. Check your plan options to keep going.\",\n                ),\n                timestamp: Date.now(),\n              },\n            });\n            return;\n          }\n          throw new Error(\n            detail ?? `AI engine request failed: ${response.status}`,\n          );\n        }\n\n        let receivedResult = false;\n        const toolsUsed: string[] = [];\n\n        await consumeSSEStream(response, {\n          onProgress: (data) => {\n            if (\n              data.phase === AiWorkflowPhase.EXECUTING_TOOL &&\n              typeof data.tool === \"string\"\n            ) {\n              toolsUsed.push(data.tool);\n            }\n            const progressItem: AiWorkflowProgress = {\n              phase: data.phase as AiWorkflowPhase,\n              tool: data.tool,","sourceCodeStart":597,"sourceCodeEnd":633,"githubUrl":"https://github.com/Stirling-Tools/Stirling-PDF/blob/9ef20dcab80b85041912f045e17a6aea1d08f969/frontend/editor/src/proprietary/components/chat/ChatContext.tsx#L597-L633","documentation":"Thrown when the AI orchestration streaming endpoint returns a non-2xx response and no usable detail message could be extracted from the JSON body. The code attempts to read body.message/detail/error/errors[0].message; if all are absent it falls back to 'AI engine request failed: {status}'.","triggerScenarios":"POST to /api/v1/ai/orchestrate/stream returns 4xx/5xx (e.g. 500 internal error, 400 bad request, 401/403 auth failure that is not the 402 usage-limit case). The response body either is not JSON or lacks a message/detail/error field.","commonSituations":"The Python AI engine (port 5001) is down or crashed. The Java backend proxy failed to reach the engine. Authentication token expired (401). Malformed request payload (400). Engine timeout (504).","solutions":["Check that the AI engine is running (task engine:dev, localhost:5001 health).","Inspect browser DevTools Network tab for the actual status code and response body to find the real cause.","Verify the auth token is valid and not expired (re-login).","Check backend logs for the proxied request error.","If status is 402 with a usage-limit code, ensure isPaygLimitCode handles the sentinel correctly."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  const response = await fetch(url, opts);\n  if (!response.ok) {\n    let detail: string | undefined;\n    try { detail = (await response.json())?.message; } catch { /* non-JSON */ }\n    throw new Error(detail ?? `AI engine request failed: ${response.status}`);\n  }\n  // consume stream...\n} catch (e) {\n  if ((e as Error).name === \"AbortError\") return;\n  showUserError(e as Error);\n}","preventionTips":["Always parse the error body for a server-provided message before falling back to a status string.","Distinguish auth (401/403), usage-limit (402), and server (5xx) errors with distinct user guidance.","Verify the AI engine health before allowing chat submissions."],"tags":["network","ai-engine","http","error-handling","streaming"],"backgroundTag":null,"analyzedSha":"9ef20dcab80b85041912f045e17a6aea1d08f969","analyzedAt":"2026-08-13T22:11:39.827Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}