stablyai/orca · error · Error

pre-mutation hooks/list reported ${byOldKey.size} of ${reque

Error message

pre-mutation hooks/list reported ${byOldKey.size} of ${request.moves.length} moved user hooks

What it means

Thrown by inspectUserHookTrust when the pre-mutation hooks/list call did not return all expected user hooks matched by their oldKey (and matching command). matchingListings builds a map keyed by normalized old key with matching command; if its size is less than moves.length, some hooks Orca expected to relocate are absent or have a different command — refusing to proceed avoids rebasing trust for a partial/inconsistent set.

Source

Thrown at src/main/codex/codex-user-hook-trust-rebase-client.ts:111

        (listing) => expected.get(normalizeHookTrustKeyForLookup(listing.key)) === listing.command
      )
      .map((listing) => [normalizeHookTrustKeyForLookup(listing.key), listing])
  )
}

function quotedKeyPath(key: string): string {
  const escaped = key.replaceAll('\\', '\\\\').replaceAll('"', '\\"')
  return `hooks.state."${escaped}"`
}

async function inspectUserHookTrust(
  request: CodexUserHookTrustInspectRequest
): Promise<CodexUserHookTrustRebaseResult> {
  return runCodexAppServerSession(request.invocation, async ({ request: requestRpc }) => {
    const result = await requestRpc('hooks/list', { cwds: [request.hooksListCwd] })
    const byOldKey = matchingListings(collectCodexHookListings(result), request.moves, 'oldKey')
    if (byOldKey.size !== request.moves.length) {
      throw new Error(
        `pre-mutation hooks/list reported ${byOldKey.size} of ${request.moves.length} moved user hooks`
      )
    }
    return {
      outcome: 'inspected',
      moves: request.moves.map((move) => {
        const listing = byOldKey.get(normalizeHookTrustKeyForLookup(move.oldKey))!
        return {
          ...move,
          reportedOldKey: listing.key,
          wasTrusted: listing.trustStatus === 'trusted',
          enabled: listing.enabled
        }
      })
    }
  })
}

View on GitHub (pinned to 1136503c6a)

Solutions

  1. Re-derive request.moves from a fresh hooks/list so oldKey and command match the live state.
  2. Ensure hooksListCwd matches the cwd used when the moves were planned.
  3. Remove from moves any hooks the user has since deleted.
  4. Confirm normalizeHookTrustKeyForLookup matches codex's current key normalization.
  5. Retry when no concurrent edit is in progress.
Defensive patterns

Strategy: validation

Validate before calling

// Re-derive moves from a live hooks/list so oldKey/command match before inspecting:
const live = collectCodexHookListings(await requestRpc('hooks/list', { cwds: [hooksListCwd] }))
const moves = plannedMoves.filter((m) =>
  live.some((l) => normalizeHookTrustKeyForLookup(l.key) === normalizeHookTrustKeyForLookup(m.oldKey) && l.command === m.command))
if (moves.length < plannedMoves.length) {
  // some hooks vanished; inspect with the filtered set instead of throwing
}

Try / catch

try {
  await runCodexUserHookTrustRebaseSession(request)
} catch (error) {
  if (error instanceof Error && error.message.startsWith('pre-mutation hooks/list reported')) {
    // re-read hooks.json, rebuild moves, retry once
  } else throw error
}

Prevention

When it happens

Trigger: A user hook listed in request.moves was removed or renamed before the inspect call; the hook's command string changed so matchingListings (which requires command equality) excludes it; hooks/list didn't report a user-scope hook because hooksListCwd differs; normalizeHookTrustKeyForLookup collides two keys.

Common situations: Concurrent edit to hooks.json removed a hook between planning the moves and inspecting; the move's oldKey/command are stale relative to the live file; a codex version reports hook keys in a different normalized form; hooksListCwd points at a directory whose project hooks differ.

Related errors


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