paperclipai/paperclip · error · Error

Photon state record is too large

Error message

Photon state record is too large

What it means

PhotonState.update() performs a compare-and-set update of a JSON-serialized record in the shared chat-sdk state store. Before writing, it checks the serialized value size and throws this plain Error if it exceeds the hard cap of 512 KB. The store's rows must stay small (they share the company/endpoint scoped CAS table), so oversized state documents are rejected rather than persisted.

Solutions

  1. Move large payloads out of PhotonState into external storage and store only a reference/ID in the state record
  2. Cap or prune collections inside the updater (e.g. keep only the last N entries) before returning the value
  3. Split one large key into multiple smaller keys so each record stays under 512 KB
  4. Add a size guard/log in the updater to detect which field is growing unboundedly

Example fix

// before: unbounded accumulation
await state.update("events", (cur) => [...(cur ?? []), event]);
// after: bounded window, overflow to external store
await state.update("events", (cur) => {
  const next = [...(cur ?? []), eventRef];
  return next.length > 500 ? next.slice(next.length - 500) : next;
});
Defensive patterns

Strategy: validation

Validate before calling

function assertStateSize<T>(value: T, label: string): T {
  const bytes = Buffer.byteLength(JSON.stringify(value));
  if (bytes > 480 * 1024) // margin under the 512 KB hard cap
    throw new Error(`${label} state would be ${bytes} bytes; prune or externalize before update()`);
  return value;
}
// usage: await state.update("events", (cur) => assertStateSize(next(cur), "events"));

Type guard

function isStateTooLargeError(e: unknown): boolean {
  return e instanceof Error && e.message === "Photon state record is too large";
}

Try / catch

try {
  await state.update(key, updater);
} catch (e) {
  if (isStateTooLargeError(e)) {
    await externalizeOverflow(key); // move big entries to object storage, keep refs
    await state.update(key, pruneToCap);
  } else throw e;
}

Prevention

When it happens

Trigger: Calling update() with an updater function that returns a value whose JSON.stringify exceeds 512 * 1024 bytes — e.g. appending unbounded items to an array, accumulating logs/events in state, or storing large blobs (attachments, long transcripts) instead of references.

Common situations: An append-only list that grows across many runs until it crosses the cap; storing full agent transcripts or large tool outputs in state instead of an external store; a bug causing duplicate entries to be merged on every update.

Understand the failure class

Background: "File too large" / "file size exceeds limit" errors: why libraries cap file sizes and how to fix them — this error's family across 46 libraries.

Related errors


AI-assisted analysis of paperclipai/paperclip@3f1d897a7c (2026-09-18). Data as JSON: /api/errors/4a0fc2cc8c4fe299. Report an issue: GitHub.

Appendix: source

Thrown at server/src/services/photon/state.ts:25

/** Typed provider records share the existing company/endpoint scoped CAS store. */
export class PhotonState {
  constructor(
    readonly scope: ChatSdkStateScope,
    private readonly persistence: ChatSdkStatePersistence,
  ) {}
  private key(key: string): string {
    return `photon:${createHash("sha256").update(key).digest("hex")}`;
  }
  async read<T>(key: string): Promise<T | null> {
    const row = await this.persistence.read(this.scope, this.key(key));
    return row ? (row.value as T) : null;
  }
  async update<T>(key: string, update: (current: T | null) => T): Promise<T> {
    for (let attempt = 0; attempt < 32; attempt++) {
      const row = await this.persistence.read(this.scope, this.key(key));
      const value = update(row ? (row.value as T) : null);
      if (Buffer.byteLength(JSON.stringify(value)) > 512 * 1024)
        throw new Error("Photon state record is too large");
      if (
        await this.persistence.compareAndSet({
          ...this.scope,
          key: this.key(key),
          expectedVersion: row?.version ?? null,
          expiresAt: null,
          value,
        })
      )
        return value;
    }
    throw new Error("Photon state changed concurrently; retry");
  }
}

View on GitHub (pinned to 3f1d897a7c)