{"record":{"id":"6524a7503651c8ac","repo":"stablyai/orca","slug":"gitea-request-failed-http-response-status","errorCode":null,"errorMessage":"Gitea request failed: HTTP ${response.status}","messagePattern":"Gitea request failed: HTTP (.+?)","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/main/gitea/client.ts","lineNumber":101,"sourceCode":"  options: RequestOptions = {},\n  // Why: the existing-review lookup behind Create must distinguish a real\n  // transport/auth failure from an accepted \"no PR\". When true, a failed request\n  // throws instead of collapsing to null so callers never report false not_found.\n  throwOnFailure = false\n): Promise<T | null> {\n  const config = getAuthConfig()\n  try {\n    const response = await fetch(apiUrl(baseUrl, path, options.searchParams), {\n      headers: {\n        Accept: 'application/json',\n        ...authHeaders(config)\n      },\n      signal: AbortSignal.timeout(options.timeoutMs ?? REQUEST_TIMEOUT_MS)\n    })\n    if (!response.ok) {\n      await cancelUnreadResponseBody(response)\n      if (throwOnFailure) {\n        throw new Error(`Gitea request failed: HTTP ${response.status}`)\n      }\n      return null\n    }\n    return (await response.json()) as T\n  } catch (error) {\n    if (throwOnFailure) {\n      throw error\n    }\n    return null\n  }\n}\n\nfunction requestJson<T>(\n  repo: GiteaRepoRef,\n  path: string,\n  options: RequestOptions = {},\n  throwOnFailure = false\n): Promise<T | null> {","sourceCodeStart":83,"sourceCodeEnd":119,"githubUrl":"https://github.com/stablyai/orca/blob/1136503c6a231a16dce8f921f6fadb63d181e8db/src/main/gitea/client.ts#L83-L119","documentation":"Thrown by requestJsonAtBase in the Gitea client when the HTTP response is not ok AND the caller passed throwOnFailure=true. The throwOnFailure flag exists specifically for the existing-review lookup behind PR Create, which must distinguish a genuine transport/auth failure (this throw) from an accepted 'no PR exists' (the null return path). The status code is included verbatim so the caller can classify 401/403/404/5xx.","triggerScenarios":"Gitea/Forgejo token is missing, expired, or lacks scope (401/403); the API base URL is misconfigured (404); the self-hosted instance is erroring (500/502/503); network proxy returning a non-2xx intercept page.","commonSituations":"ORCA_GITEA_TOKEN env var unset or revoked; ORCA_GITEA_API_BASE_URL pointing at the wrong path (missing /api/v1); token scopes narrowed after a Forgejo upgrade; reverse proxy rate-limiting returning 429.","solutions":["Check the HTTP status: 401/403 → fix ORCA_GITEA_TOKEN; 404 → fix ORCA_GITEA_API_BASE_URL (ensure it normalizes to include /api/v1); 5xx → check the Gitea instance.","Verify the token with `curl -H \"Authorization: token $ORCA_GITEA_TOKEN\" $ORCA_GITEA_API_BASE_URL/user`.","Confirm the base URL does not already include a trailing /api/v1 twice (normalizeGiteaApiBaseUrl appends one only if absent).","If 429, back off — the client uses a 5s default and 15s for PR list pages."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"function giteaConfigLooksValid(): boolean {\n  const base = (process.env.ORCA_GITEA_API_BASE_URL ?? '').trim()\n  const token = (process.env.ORCA_GITEA_TOKEN ?? '').trim()\n  return base.length > 0 && token.length > 0\n}","typeGuard":null,"tryCatchPattern":"try {\n  const review = await requestJsonAtBase(baseUrl, path, opts, /*throwOnFailure*/ true)\n} catch (err) {\n  const status = /HTTP (\\d{3})/.exec((err as Error).message)?.[1]\n  if (status === '401' || status === '403') promptReconfigureToken()\n  else if (status === '404') promptReconfigureBaseUrl()\n  else toast.error(`Gitea request failed (${status})`)\n}","preventionTips":["Set ORCA_GITEA_API_BASE_URL and ORCA_GITEA_TOKEN in the environment.","Use a token with the minimal required scopes; rotate before expiry.","Confirm the base URL normalizes to include /api/v1 exactly once."],"tags":["gitea","http","auth","network","configuration"],"backgroundTag":null,"analyzedSha":"1136503c6a231a16dce8f921f6fadb63d181e8db","analyzedAt":"2026-08-12T23:15:58.167Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}