{"record":{"id":"67f612355fca5a1e","repo":"stablyai/orca","slug":"gitlab-returned-a-non-list-response-payload-tri","errorCode":null,"errorMessage":"GitLab returned a non-list response: ${payload.trim().slice(0, REPORTED_PAYLOAD_LIMIT)}","messagePattern":"GitLab returned a non-list response: (.+?)","errorType":"exception","errorClass":"GlabNonListResponseError","httpStatus":null,"severity":"error","filePath":"src/main/gitlab/glab-api-response.ts","lineNumber":51,"sourceCode":"\n/**\n * Parse a glab list response, failing readably when GitLab answers with a JSON object.\n *\n * Why: glab exits 0 on error envelopes and proxy wrappers, so `JSON.parse(...).map` threw an\n * opaque `.map is not a function` that the caller's classifier could only report as \"unknown\".\n */\nexport function parseGlabJsonList<T>(payload: string): T[] {\n  const parsed: unknown = JSON.parse(payload)\n  if (Array.isArray(parsed)) {\n    return parsed as T[]\n  }\n  const reported = gitlabErrorText(parsed)\n  if (reported) {\n    throw new Error(`GitLab returned an error: ${reported}`)\n  }\n  // Why: slice the raw payload rather than re-serializing `parsed` — same text, without\n  // stringifying a multi-megabyte body just to keep the preview.\n  throw new GlabNonListResponseError(\n    `GitLab returned a non-list response: ${payload.trim().slice(0, REPORTED_PAYLOAD_LIMIT)}`\n  )\n}\n\n/** GitLab reports API failures as `{ message }` or `{ error }`; anything else is opaque data. */\nfunction gitlabErrorText(parsed: unknown): string | null {\n  if (typeof parsed !== 'object' || parsed === null) {\n    return null\n  }\n  const { message, error } = parsed as { message?: unknown; error?: unknown }\n  for (const value of [message, error]) {\n    if (typeof value === 'string' && value.trim()) {\n      return value.trim().slice(0, REPORTED_PAYLOAD_LIMIT)\n    }\n  }\n  return null\n}\n","sourceCodeStart":33,"sourceCodeEnd":69,"githubUrl":"https://github.com/stablyai/orca/blob/1136503c6a231a16dce8f921f6fadb63d181e8db/src/main/gitlab/glab-api-response.ts#L33-L69","documentation":"Thrown by parseGlabJsonList (as GlabNonListResponseError) when the parsed JSON is neither an array nor a recognizable GitLab error object — i.e. opaque non-list data. This is the fallback after gitlabErrorText returns null, meaning the payload has no usable `message`/`error` string. The raw payload is sliced (not re-serialized) to REPORTED_PAYLOAD_LIMIT to keep multi-megabyte bodies out of the error banner.","triggerScenarios":"glab returned a JSON object or scalar with no `message`/`error` field: a single resource object where a list was expected, a proxy HTML/JSON interstitial, a `{ data: [...] }` wrapper, or a status object. Also when an endpoint path mismatch returns a single entity instead of a collection.","commonSituations":"Calling a show/detail endpoint with a list parser; corporate proxy returning a captive-portal JSON; GitLab version returning a paginated wrapper `{ \"data\": [...], \"pagination\": {...} }` instead of a bare array; misconfigured endpoint path hitting a different resource.","solutions":["Inspect the sliced payload in the error message to identify the actual shape returned.","Correct the endpoint/path so GitLab returns a bare array (e.g. use the collection URL, not the single-resource URL).","If a wrapper object is expected, unwrap it before calling parseGlabJsonList, or use a different parser for that endpoint.","Confirm the request reaches real GitLab and not a proxy interstitial."],"exampleFix":null,"handlingStrategy":"type-guard","validationCode":"function isGlabListPayload(payload: string): boolean {\n  try { return Array.isArray(JSON.parse(payload)) } catch { return false }\n}","typeGuard":"function isGlabList<T>(parsed: unknown): parsed is T[] {\n  return Array.isArray(parsed)\n}","tryCatchPattern":"try {\n  const items = parseGlabJsonList<T>(payload)\n} catch (error) {\n  if (error instanceof GlabNonListResponseError) {\n    // log the sliced payload, flag the endpoint/path for review\n  }\n}","preventionTips":["Use list/collection endpoints (not show endpoints) with list parsers.","Unwrap known wrapper objects ({ data: [...] }) before calling the list parser.","Watch for proxy interstitials in corporate networks that replace GitLab JSON."],"tags":["gitlab","glab","api","response-shape","parser"],"backgroundTag":null,"analyzedSha":"1136503c6a231a16dce8f921f6fadb63d181e8db","analyzedAt":"2026-08-12T23:15:58.167Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}