{"record":{"id":"6da6a138e52c4128","repo":"can1357/oh-my-pi","slug":"devin-api-error-response-status-response-stat","errorCode":null,"errorMessage":"Devin API error ${response.status} ${response.statusText}: ${text}","messagePattern":"Devin API error (.+?) (.+?): (.+?)","errorType":"exception","errorClass":"AIError.DevinApiError","httpStatus":null,"severity":"error","filePath":"packages/ai/src/providers/devin.ts","lineNumber":214,"sourceCode":"\n\t\t\tconst response = await fetchImpl(chatBaseUrl + CHAT_MESSAGE_PATH, {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders: {\n\t\t\t\t\t\"content-type\": \"application/connect+proto\",\n\t\t\t\t\t\"connect-protocol-version\": \"1\",\n\t\t\t\t\t\"connect-content-encoding\": \"gzip\",\n\t\t\t\t\t\"accept-encoding\": \"identity\",\n\t\t\t\t\t\"user-agent\": \"connect-go/1.18.1 (go1.26.3)\",\n\t\t\t\t\t\"connect-accept-encoding\": \"gzip\",\n\t\t\t\t\t...(options?.headers ?? {}),\n\t\t\t\t},\n\t\t\t\tbody: frame,\n\t\t\t\tsignal: options?.signal,\n\t\t\t});\n\n\t\t\tif (!response.ok) {\n\t\t\t\tconst text = await response.text();\n\t\t\t\tthrow new AIError.DevinApiError(\n\t\t\t\t\t`Devin API error ${response.status} ${response.statusText}: ${text}`,\n\t\t\t\t\tresponse.status,\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (!response.body) {\n\t\t\t\tthrow new AIError.ProviderResponseError(\"Devin API error: response body is empty\", {\n\t\t\t\t\tprovider: model.provider,\n\t\t\t\t\tkind: \"empty-body\",\n\t\t\t\t});\n\t\t\t}\n\t\t\tconst body = response.body;\n\n\t\t\tstream.push({ type: \"start\", partial: output });\n\n\t\t\tconst reader = body.getReader();\n\t\t\tlet pending = Buffer.alloc(0);\n\n\t\t\tfor (;;) {","sourceCodeStart":196,"sourceCodeEnd":232,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/ai/src/providers/devin.ts#L196-L232","documentation":"The Devin provider received a non-2xx HTTP response from the Devin API and wraps the full status line plus response body text in a DevinApiError. The body text usually contains Devin's JSON error detail explaining the actual failure (auth, quota, invalid payload, etc.).","triggerScenarios":"streamDevin sends a request frame and response.ok is false — e.g. 401 invalid API key, 403 forbidden, 429 rate limited, 400 malformed request body, or 5xx Devin server error.","commonSituations":"Expired or wrong Devin API credentials; exceeding session/rate quotas; sending a payload the API rejects after a schema change; Devin service outage returning 5xx.","solutions":["Read the error message's embedded response text — it contains Devin's specific error code/reason.","If 401/403, verify and rotate your Devin API key.","If 429, back off and retry with exponential delay; respect rate-limit headers.","If 400, validate the request frame against the current Devin API schema.","If 5xx, retry later and check Devin's status page for incidents."],"exampleFix":"// before\nawait streamDevin(model, ctx, { apiKey: staleKey });\n// after\nconst key = await refreshDevinApiKey();\nawait streamDevin(model, ctx, { apiKey: key });","handlingStrategy":"try-catch","validationCode":"if (!process.env.DEVIN_API_KEY) throw new Error(\"Devin API key must be configured before calling the API.\");","typeGuard":"null","tryCatchPattern":"try {\n  await streamDevin(model, ctx, options);\n} catch (err) {\n  if (err instanceof AIError.DevinApiError) {\n    if (err.status === 429) await backoffAndRetry();\n    else if (err.status === 401 || err.status === 403) refreshCredentials();\n    else logDevinApiError(err.message); // includes response body detail\n  } else throw err;\n}","preventionTips":["Validate the Devin API key before calls and rotate it before expiry.","Implement rate-limit-aware backoff for 429 responses.","Log the embedded response body for actionable diagnostics."],"tags":["network","http-error","devin","api-error"],"backgroundTag":"http-4xx-5xx-api-error","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}