paperclipai/paperclip · error · Error

Discord command registration persistence unproven

Error message

Discord command registration persistence unproven

What it means

The internal persist() step calls the caller-supplied commit(state, next) hook to durably store the new registration state; any throw is converted to 'Discord command registration persistence unproven'. Because the next in-memory state is only adopted after commit succeeds, a failure means the new phase could not be proven durable and reconciliation aborts.

Source

Thrown at server/src/services/chat-discord-command-registration.ts:449

    ...input,
    scope: state.scope,
    state,
    runtimeFence: Object.freeze(runtimeFence.data),
    verifiedIdentity: Object.freeze({ ...input.verifiedIdentity }),
  };
  const authorize = async (stage: DiscordCommandRegistrationStage) => {
    try {
      await input.authorize(stage);
    } catch {
      throw new Error("Discord command registration authorization denied");
    }
  };
  const persist = async (next: DiscordCommandRegistration) => {
    freezeState(next);
    try {
      await input.commit(state!, next);
    } catch {
      throw new Error("Discord command registration persistence unproven");
    }
    state = next;
  };
  const settle = async (
    command: RemoteCommand,
  ): Promise<DiscordCommandRegistrationResult> => {
    const next: Extract<DiscordCommandRegistration, { phase: "registered" }> = {
      schema: state!.schema,
      scope: state!.scope,
      ownerId: state!.ownerId,
      phase: "registered",
      receipt: {
        commandId: command.id,
        version: command.version,
        definitionDigest: definitionDigest(state!.ownerId),
      },
    };
    freezeState(next);

View on GitHub (pinned to 01ad858492)

Solutions

  1. Check the commit hook's own storage layer for the underlying error (it is swallowed here).
  2. Retry reconciliation once the database is healthy; state is unchanged on failure so it is safe.
  3. Fix constraint/schema mismatches between the state blob and the persistence table.
  4. Make the commit hook idempotent and retryable for transient DB errors.

Example fix

// before (app's commit hook)
await db.insert(registrations).values(next); // throws on conflict
// after
await db.insert(registrations)
  .values(next)
  .onConflictDoUpdate({ target: registrations.key, set: next });
Defensive patterns

Strategy: retry

Try / catch

try { await reconcileDiscordCommandRegistration(input); }
catch (e) {
  if ((e as Error).message === "Discord command registration persistence unproven") {
    await sleep(backoff()); return retry(); // in-memory state unchanged; safe to retry
  }
  throw e;
}

Prevention

When it happens

Trigger: The commit callback throws — database unavailable, unique-constraint conflict, transaction deadlock, serialization failure, or the hook rejecting the frozen next state blob.

Common situations: Postgres connection pool exhaustion or failover during reconciliation; a migration/unique index conflict on the state row; the storage layer enforcing a schema the new state doesn't match; network blip between server and DB.

Related errors


AI-assisted analysis of paperclipai/paperclip@01ad858492 (2026-09-10). Data as JSON: /api/errors/a8b7ad08cc9c024e. Report an issue: GitHub.