stablyai/orca · error

malformed stack

Error message

malformed stack

What it means

Thrown by getRestPRByNumber when requireUsableStackMetadata is set, restData.stack is present and non-null, but isUsableRestStackMetadata rejects it OR mapRestPullRequest did not produce a stack. GitHub omits the stack field for ordinary PRs (which is fine — that path is skipped); only a non-null but unusable value triggers this. It guards against partial or malformed stack metadata that would render an inconsistent PR stack view.

Source

Thrown at src/main/github/client.ts:3008

): Promise<PullRequestLookupData> {
  const { stdout } = await ghExecFileAsync(
    ['api', `repos/${ownerRepo.owner}/${ownerRepo.repo}/pulls/${number}`],
    { ...ghOptions, ...githubHostExecOptions(ownerRepo) }
  )
  const parsed = JSON.parse(stdout) as unknown
  if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
    throw new Error('invalid response shape')
  }
  const restData = parsed as RestPullRequest
  const mapped = mapRestPullRequest(restData)
  if (
    options.requireUsableStackMetadata &&
    restData.stack !== undefined &&
    restData.stack !== null
  ) {
    // Why: GitHub omits stack for ordinary PRs; only unusable non-null metadata is unsafe.
    if (!isUsableRestStackMetadata(restData.stack) || !mapped.stack) {
      throw new Error('malformed stack')
    }
    if (!isGitObjectId(restData.head?.sha)) {
      throw new Error('missing head SHA')
    }
  }
  return mapped
}

function prunePRStackSummaryCache(now = Date.now()): void {
  for (const [key, cached] of prStackSummaryCache) {
    if (cached.expiresAt <= now) {
      prStackSummaryCache.delete(key)
    }
  }
  while (prStackSummaryCache.size > PR_STACK_SUMMARY_CACHE_MAX_ENTRIES) {
    const oldestKey = prStackSummaryCache.keys().next().value
    if (oldestKey === undefined) {
      return

View on GitHub (pinned to 1136503c6a)

Solutions

  1. Inspect the raw `stack` field from `gh api repos/<owner>/<repo>/pulls/<n>` and compare against isUsableRestStackMetadata's requirements.
  2. If the shape is genuinely new, file an issue — the mapper may need to recognize the new format.
  3. As a workaround, calls without requireUsableStackMetadata will tolerate the field; use that path for display-only contexts.
Defensive patterns

Strategy: validation

Validate before calling

import { isUsableRestStackMetadata } from './stack-metadata'
function stackIsUsable(stack: unknown): boolean {
  return stack == null || isUsableRestStackMetadata(stack)
}

Type guard

function isUsableRestStackMetadataShape(stack: unknown): boolean {
  return typeof stack === 'object' && stack !== null &&
    Array.isArray((stack as any).entries) && (stack as any).entries.length > 0
}

Try / catch

try {
  const pr = await getRestPRByNumber(ownerRepo, number, ghOptions, { requireUsableStackMetadata: true })
} catch (err) {
  if ((err as Error).message === 'malformed stack') {
    // fall back to non-stack-aware lookup for display only
    return getRestPRByNumber(ownerRepo, number, ghOptions)
  }
  throw err
}

Prevention

When it happens

Trigger: GitHub returns a stack object missing required child entries; the stack payload references PR numbers that didn't map; a beta stack feature returned a shape the mapper doesn't yet understand; the gh CLI or GitHub Enterprise served a partially-serialized stack.

Common situations: GitHub rolls out a stack-format change; a stacked-PR tool (Graphite, gh-stack) writes metadata in a non-standard shape; Enterprise Server lags behind on stack API fields.

Understand the failure class

Related errors


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