stablyai/orca · error · Error

Codex reset attempt idempotency key is invalid

Error message

Codex reset attempt idempotency key is invalid

What it means

Thrown by `getOrCreateCodexResetAttempt` when a new attempt must be created (no existing entry) and the caller-supplied `createIdempotencyKey()` returns a value that fails `IdempotencyKeySchema` — which is `z.uuid()`. The idempotency key is what makes the downstream reset RPC safely retryable after a lost response, so it must be a well-formed UUID before it is ever persisted.

Source

Thrown at mobile/src/storage/codex-reset-attempt-journal.ts:147

    if (scopeMutations.get(key) === tail) {
      scopeMutations.delete(key)
    }
  }
}

export async function getOrCreateCodexResetAttempt(
  identity: AttemptIdentity & { createIdempotencyKey: () => string }
): Promise<CodexResetAttempt> {
  return withScopeMutation(identity, async () => {
    const key = storageKey(identity)
    const raw = await AsyncStorage.getItem(key)
    if (raw !== null) {
      return parseAttempt(raw, identity)
    }

    const idempotencyKey = identity.createIdempotencyKey()
    if (!IdempotencyKeySchema.safeParse(idempotencyKey).success) {
      throw new Error('Codex reset attempt idempotency key is invalid')
    }
    const attempt = CodexResetAttemptSchema.parse({
      v: 1,
      hostId: identity.hostId,
      expectedScope: identity.expectedScope,
      idempotencyKey
    })
    // Why: the key must survive a committed provider mutation whose response is
    // lost; no reset RPC may start until this write has completed successfully.
    await AsyncStorage.setItem(key, JSON.stringify(attempt))
    return attempt
  })
}

export async function clearCodexResetAttemptAfterAuthoritativeResponse(
  identity: AttemptIdentity & { idempotencyKey: string }
): Promise<void> {
  return withScopeMutation(identity, async () => {

View on GitHub (pinned to 1136503c6a)

Solutions

  1. Make `createIdempotencyKey` return a canonical lowercase v4 UUID (e.g. via `crypto.randomUUID()` or `uuid.v4()`).
  2. Add a unit test asserting the generator output passes `z.uuid()`.
  3. Strip braces and lowercase the value before returning if using a legacy UUID formatter.
  4. If using a custom generator, validate it against the UUID regex `^[0-9a-f]{8}-...$` upstream.

Example fix

// before — non-UUID key
createIdempotencyKey: () => `${Date.now()}`

// after — canonical UUID
createIdempotencyKey: () => crypto.randomUUID()
Defensive patterns

Strategy: validation

Validate before calling

// Validate the key shape before it reaches the journal.
import { z } from 'zod'
const IdempotencyKeySchema = z.uuid()
const key = identity.createIdempotencyKey()
if (!IdempotencyKeySchema.safeParse(key).success) {
  throw new Error('createIdempotencyKey must return a canonical UUID')
}

Type guard

function isUuid(value: string): boolean {
  return /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(value)
}

Prevention

When it happens

Trigger: First-time reset for an account scope where AsyncStorage has no entry, and the `createIdempotencyKey` callback returns a non-UUID string (wrong format, empty, contains invalid characters, or not lowercase canonical form).

Common situations: The key generator uses a non-UUID source (timestamp, random hex of wrong length); a UUID library returns an uppercased or braced form that `z.uuid()` rejects; the callback was stubbed incorrectly in tests; v4 vs v1 UUID format mismatch.

Related errors


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