stablyai/orca · error · GlabNonListResponseError

GitLab returned a non-list response: ${payload.trim().slice(

Error message

GitLab returned a non-list response: ${payload.trim().slice(0, REPORTED_PAYLOAD_LIMIT)}

What it means

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.

Source

Thrown at src/main/gitlab/glab-api-response.ts:51

/**
 * Parse a glab list response, failing readably when GitLab answers with a JSON object.
 *
 * Why: glab exits 0 on error envelopes and proxy wrappers, so `JSON.parse(...).map` threw an
 * opaque `.map is not a function` that the caller's classifier could only report as "unknown".
 */
export function parseGlabJsonList<T>(payload: string): T[] {
  const parsed: unknown = JSON.parse(payload)
  if (Array.isArray(parsed)) {
    return parsed as T[]
  }
  const reported = gitlabErrorText(parsed)
  if (reported) {
    throw new Error(`GitLab returned an error: ${reported}`)
  }
  // Why: slice the raw payload rather than re-serializing `parsed` — same text, without
  // stringifying a multi-megabyte body just to keep the preview.
  throw new GlabNonListResponseError(
    `GitLab returned a non-list response: ${payload.trim().slice(0, REPORTED_PAYLOAD_LIMIT)}`
  )
}

/** GitLab reports API failures as `{ message }` or `{ error }`; anything else is opaque data. */
function gitlabErrorText(parsed: unknown): string | null {
  if (typeof parsed !== 'object' || parsed === null) {
    return null
  }
  const { message, error } = parsed as { message?: unknown; error?: unknown }
  for (const value of [message, error]) {
    if (typeof value === 'string' && value.trim()) {
      return value.trim().slice(0, REPORTED_PAYLOAD_LIMIT)
    }
  }
  return null
}

View on GitHub (pinned to 1136503c6a)

Solutions

  1. Inspect the sliced payload in the error message to identify the actual shape returned.
  2. Correct the endpoint/path so GitLab returns a bare array (e.g. use the collection URL, not the single-resource URL).
  3. If a wrapper object is expected, unwrap it before calling parseGlabJsonList, or use a different parser for that endpoint.
  4. Confirm the request reaches real GitLab and not a proxy interstitial.
Defensive patterns

Strategy: type-guard

Validate before calling

function isGlabListPayload(payload: string): boolean {
  try { return Array.isArray(JSON.parse(payload)) } catch { return false }
}

Type guard

function isGlabList<T>(parsed: unknown): parsed is T[] {
  return Array.isArray(parsed)
}

Try / catch

try {
  const items = parseGlabJsonList<T>(payload)
} catch (error) {
  if (error instanceof GlabNonListResponseError) {
    // log the sliced payload, flag the endpoint/path for review
  }
}

Prevention

When it happens

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

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

Related errors


AI-assisted analysis of stablyai/orca@1136503c6a (2026-08-12). Data as JSON: /api/errors/67f612355fca5a1e. Report an issue: GitHub.