stablyai/orca · error · Error

post-mutation hooks/list reported

Error message

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

What it means

Thrown by repairUserHookTrust when the post-mutation hooks/list call did not return all expected user hooks matched by their newKey (and matching command). After hooks.json was mutated to relocate hooks, codex's hooks/list should report each hook under its new key; a shortfall means the mutation didn't land as expected and rebasing trust now would be inconsistent.

Solutions

  1. Verify the hooks.json write actually persisted the new keys (read the file back).
  2. Ensure reloadUserConfig / codex config reload ran so hooks/list reflects the new state.
  3. Confirm newKey values don't collide with pre-existing hook keys.
  4. Check that normalizeHookTrustKeyForLookup and the command string still match what codex reports post-mutation.
  5. Retry the repair once the file is stable and reloaded.
Defensive patterns

Strategy: try-catch

Validate before calling

// After the hooks.json mutation, verify the write persisted before calling repair:
const { config } = readHooksJsonWithRaw(hooksJsonPath)
const allNewKeysPresent = request.moves.every((m) =>
  Object.values(config?.hooks ?? {}).flat().some((h) => normalizeKey(h) === normalizeHookTrustKeyForLookup(m.newKey)))
if (!allNewKeysPresent) {
  // abort repair; the mutation didn't land
}

Try / catch

try {
  await runCodexUserHookTrustRebaseSession(request)
} catch (error) {
  if (error instanceof Error && error.message.startsWith('post-mutation hooks/list reported')) {
    // confirm reloadUserConfig applied; retry after codex reloads config
  } else throw error
}

Prevention

When it happens

Trigger: The hooks.json write that moved hooks to newKey didn't take effect (write failed silently, codex didn't reload, file generation changed); a hook's newKey collides with an existing key; codex normalized the new key differently than expected; the matching command drifted.

Common situations: assertHooksJsonGeneration aborted the write due to a concurrent edit; config/reload didn't pick up the change; codex reports the relocated hook under a transformed key; a second hook already occupied the new key.

Related errors


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

Appendix: source

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

        return {
          ...move,
          reportedOldKey: listing.key,
          wasTrusted: listing.trustStatus === 'trusted',
          enabled: listing.enabled
        }
      })
    }
  })
}

async function repairUserHookTrust(
  request: CodexUserHookTrustRepairRequest
): Promise<CodexUserHookTrustRebaseResult> {
  return runCodexAppServerSession(request.invocation, async ({ request: requestRpc }) => {
    const result = await requestRpc('hooks/list', { cwds: [request.hooksListCwd] })
    const byNewKey = matchingListings(collectCodexHookListings(result), request.moves, 'newKey')
    if (byNewKey.size !== request.moves.length) {
      throw new Error(
        `post-mutation hooks/list reported ${byNewKey.size} of ${request.moves.length} moved user hooks`
      )
    }

    const keysToClear = new Set([
      ...request.moves.map((move) => move.reportedOldKey),
      ...Array.from(byNewKey.values(), (listing) => listing.key)
    ])
    const edits: { keyPath: string; value: unknown; mergeStrategy: 'replace' }[] = Array.from(
      keysToClear,
      (key) => ({
        keyPath: quotedKeyPath(key),
        value: null,
        mergeStrategy: 'replace' as const
      })
    )
    for (const move of request.moves) {
      const listing = byNewKey.get(normalizeHookTrustKeyForLookup(move.newKey))!

View on GitHub (pinned to 1136503c6a)