{"record":{"id":"80986f792f11bdca","repo":"stablyai/orca","slug":"pre-mutation-hooks-list-reported-byoldkey-size","errorCode":null,"errorMessage":"pre-mutation hooks/list reported ${byOldKey.size} of ${request.moves.length} moved user hooks","messagePattern":"pre-mutation hooks/list reported (.+?) of (.+?) moved user hooks","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/main/codex/codex-user-hook-trust-rebase-client.ts","lineNumber":111,"sourceCode":"        (listing) => expected.get(normalizeHookTrustKeyForLookup(listing.key)) === listing.command\n      )\n      .map((listing) => [normalizeHookTrustKeyForLookup(listing.key), listing])\n  )\n}\n\nfunction quotedKeyPath(key: string): string {\n  const escaped = key.replaceAll('\\\\', '\\\\\\\\').replaceAll('\"', '\\\\\"')\n  return `hooks.state.\"${escaped}\"`\n}\n\nasync function inspectUserHookTrust(\n  request: CodexUserHookTrustInspectRequest\n): Promise<CodexUserHookTrustRebaseResult> {\n  return runCodexAppServerSession(request.invocation, async ({ request: requestRpc }) => {\n    const result = await requestRpc('hooks/list', { cwds: [request.hooksListCwd] })\n    const byOldKey = matchingListings(collectCodexHookListings(result), request.moves, 'oldKey')\n    if (byOldKey.size !== request.moves.length) {\n      throw new Error(\n        `pre-mutation hooks/list reported ${byOldKey.size} of ${request.moves.length} moved user hooks`\n      )\n    }\n    return {\n      outcome: 'inspected',\n      moves: request.moves.map((move) => {\n        const listing = byOldKey.get(normalizeHookTrustKeyForLookup(move.oldKey))!\n        return {\n          ...move,\n          reportedOldKey: listing.key,\n          wasTrusted: listing.trustStatus === 'trusted',\n          enabled: listing.enabled\n        }\n      })\n    }\n  })\n}\n","sourceCodeStart":93,"sourceCodeEnd":129,"githubUrl":"https://github.com/stablyai/orca/blob/1136503c6a231a16dce8f921f6fadb63d181e8db/src/main/codex/codex-user-hook-trust-rebase-client.ts#L93-L129","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Re-derive request.moves from a fresh hooks/list so oldKey and command match the live state.","Ensure hooksListCwd matches the cwd used when the moves were planned.","Remove from moves any hooks the user has since deleted.","Confirm normalizeHookTrustKeyForLookup matches codex's current key normalization.","Retry when no concurrent edit is in progress."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Re-derive moves from a live hooks/list so oldKey/command match before inspecting:\nconst live = collectCodexHookListings(await requestRpc('hooks/list', { cwds: [hooksListCwd] }))\nconst moves = plannedMoves.filter((m) =>\n  live.some((l) => normalizeHookTrustKeyForLookup(l.key) === normalizeHookTrustKeyForLookup(m.oldKey) && l.command === m.command))\nif (moves.length < plannedMoves.length) {\n  // some hooks vanished; inspect with the filtered set instead of throwing\n}","typeGuard":null,"tryCatchPattern":"try {\n  await runCodexUserHookTrustRebaseSession(request)\n} catch (error) {\n  if (error instanceof Error && error.message.startsWith('pre-mutation hooks/list reported')) {\n    // re-read hooks.json, rebuild moves, retry once\n  } else throw error\n}","preventionTips":["Generate request.moves from a fresh hooks/list at call time, not from a stale snapshot.","Prevent concurrent edits to hooks.json during trust rebase.","Ensure hooksListCwd matches the cwd used to plan the moves.","Filter out hooks the user has since deleted before inspecting."],"tags":["codex","hooks","trust-rebase","json-rpc","consistency"],"backgroundTag":null,"analyzedSha":"1136503c6a231a16dce8f921f6fadb63d181e8db","analyzedAt":"2026-08-12T23:15:58.167Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}