can1357/oh-my-pi · error · AIError.DevinApiError

Devin API error ${response.status} ${response.statusText}: $

Error message

Devin API error ${response.status} ${response.statusText}: ${text}

What it means

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.).

Source

Thrown at packages/ai/src/providers/devin.ts:214

			const response = await fetchImpl(chatBaseUrl + CHAT_MESSAGE_PATH, {
				method: "POST",
				headers: {
					"content-type": "application/connect+proto",
					"connect-protocol-version": "1",
					"connect-content-encoding": "gzip",
					"accept-encoding": "identity",
					"user-agent": "connect-go/1.18.1 (go1.26.3)",
					"connect-accept-encoding": "gzip",
					...(options?.headers ?? {}),
				},
				body: frame,
				signal: options?.signal,
			});

			if (!response.ok) {
				const text = await response.text();
				throw new AIError.DevinApiError(
					`Devin API error ${response.status} ${response.statusText}: ${text}`,
					response.status,
				);
			}
			if (!response.body) {
				throw new AIError.ProviderResponseError("Devin API error: response body is empty", {
					provider: model.provider,
					kind: "empty-body",
				});
			}
			const body = response.body;

			stream.push({ type: "start", partial: output });

			const reader = body.getReader();
			let pending = Buffer.alloc(0);

			for (;;) {

View on GitHub (pinned to 9690622007)

Solutions

  1. Read the error message's embedded response text — it contains Devin's specific error code/reason.
  2. If 401/403, verify and rotate your Devin API key.
  3. If 429, back off and retry with exponential delay; respect rate-limit headers.
  4. If 400, validate the request frame against the current Devin API schema.
  5. If 5xx, retry later and check Devin's status page for incidents.

Example fix

// before
await streamDevin(model, ctx, { apiKey: staleKey });
// after
const key = await refreshDevinApiKey();
await streamDevin(model, ctx, { apiKey: key });
Defensive patterns

Strategy: try-catch

Validate before calling

if (!process.env.DEVIN_API_KEY) throw new Error("Devin API key must be configured before calling the API.");

Type guard

null

Try / catch

try {
  await streamDevin(model, ctx, options);
} catch (err) {
  if (err instanceof AIError.DevinApiError) {
    if (err.status === 429) await backoffAndRetry();
    else if (err.status === 401 || err.status === 403) refreshCredentials();
    else logDevinApiError(err.message); // includes response body detail
  } else throw err;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/6da6a138e52c4128. Report an issue: GitHub.