{"record":{"id":"a50e58f0e22d0025","repo":"can1357/oh-my-pi","slug":"devin-auth-error-response-status-response-sta","errorCode":null,"errorMessage":"Devin auth error ${response.status} ${response.statusText}: ${new TextDecoder().decode(payload)}","messagePattern":"Devin auth error (.+?) (.+?): (.+?)","errorType":"exception","errorClass":"AIError.DevinApiError","httpStatus":null,"severity":"error","filePath":"packages/ai/src/providers/devin.ts","lineNumber":505,"sourceCode":"\tapiKey: string | undefined,\n\tbaseUrl: string,\n\tfetchImpl: NonNullable<StreamOptions[\"fetch\"]>,\n\tsignal: AbortSignal | undefined,\n): Promise<{ userJwt: string; baseUrl?: string }> {\n\tconst request = create(GetUserJwtRequestSchema, { metadata: create(MetadataSchema, devinCliMetadata(apiKey)) });\n\tconst response = await fetchImpl(`${baseUrl}${DEVIN_AUTH_PATH}`, {\n\t\tmethod: \"POST\",\n\t\theaders: {\n\t\t\t\"content-type\": \"application/proto\",\n\t\t\t\"connect-protocol-version\": \"1\",\n\t\t\taccept: \"*/*\",\n\t\t},\n\t\tbody: toBinary(GetUserJwtRequestSchema, request),\n\t\tsignal,\n\t});\n\tconst payload = new Uint8Array(await response.arrayBuffer());\n\tif (!response.ok) {\n\t\tthrow new AIError.DevinApiError(\n\t\t\t`Devin auth error ${response.status} ${response.statusText}: ${new TextDecoder().decode(payload)}`,\n\t\t\tresponse.status,\n\t\t);\n\t}\n\tconst decoded = decodeDevinUnaryMessage(GetUserJwtResponseSchema, payload);\n\tif (!decoded?.userJwt) {\n\t\tthrow new AIError.ProviderResponseError(\"Devin auth error: GetUserJwt returned an empty user JWT\", {\n\t\t\tprovider: \"devin\",\n\t\t\tkind: \"runtime\",\n\t\t});\n\t}\n\tconst customBaseUrl = decoded.customApiServerUrl.trim();\n\treturn { userJwt: decoded.userJwt, ...(customBaseUrl ? { baseUrl: customBaseUrl.replace(/\\/+$/, \"\") } : undefined) };\n}\n\n/**\n * Resolve a server-side router (`adaptive`) into the concrete model uid plus the\n * assignment JWT that authorizes it. The router uid is never a legal","sourceCodeStart":487,"sourceCodeEnd":523,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/ai/src/providers/devin.ts#L487-L523","documentation":"Thrown by fetchDevinAuthMetadata when the GetUserJwt HTTP call returns a non-2xx status. The response body bytes are decoded as text and embedded in the message so the caller can see the server's reason. It is a DevinApiError carrying the HTTP status code for upstream classification (e.g. auth retry logic).","triggerScenarios":"POST to Devin's GetUserJwt endpoint responds 401/403/404/500 etc., with the raw body included — typically invalid or revoked Devin API key, wrong base URL, or server-side outage.","commonSituations":"Expired or rotated Devin credentials still configured in the environment; Devin instance URL misconfigured; Devin service outage or version change altering the auth route.","solutions":["Check that the Devin API key is valid and not revoked/expired; re-provision it","Verify the Devin base URL matches your instance (self-hosted vs SaaS)","Call GetUserJwt manually (curl) to read the embedded response body message","If 5xx, retry later or check Devin service status"],"exampleFix":"// before\nprocess.env.DEVIN_API_KEY = \"stale-key\"\n// after\nprocess.env.DEVIN_API_KEY = \"<freshly-rotated devin api key>\"","handlingStrategy":"try-catch","validationCode":"// Preflight: verify the credential works before the real call\nconst res = await fetch(`${devinBaseUrl}/auth`, { headers: { authorization: `Bearer ${apiKey}` } });\nif (!res.ok) throw new Error(`Devin credential rejected: ${res.status} — rotate the API key`);","typeGuard":"function isDevinApiError(err: unknown): err is InstanceType<typeof AIError.DevinApiError> {\n  return err instanceof AIError.DevinApiError && typeof err.status === \"number\";\n}","tryCatchPattern":"try {\n  const meta = await fetchDevinAuthMetadata(baseUrl, request, signal);\n} catch (err) {\n  if (err instanceof AIError.DevinApiError && (err.status === 401 || err.status === 403)) {\n    // refresh/rotate the Devin API key, then retry once\n  } else throw err;\n}","preventionTips":["Rotate Devin API keys on a schedule and store them in a secret manager, not hard-coded","Validate the key at startup with a cheap authenticated call","Keep the Devin base URL in one config location to avoid instance mismatch","Subscribe to Devin service status for outage awareness"],"tags":["auth","http","devin","api-key"],"backgroundTag":"http-auth-error","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}