paperclipai/paperclip · warning
dropping 1 event with a non-serializable dimension (circular
Error message
dropping 1 event with a non-serializable dimension (circular reference?); event="${chunk[0]?.name}" What it means
While size-splitting a batch, the serialized byte count for a single-event envelope is not a finite number — classically because JSON.stringify failed or degenerated on a circular reference inside an event dimension. The event is dropped with this warning and never reaches the collector.
Source
Thrown at packages/shared/src/telemetry/client.ts:190
*/
private chunkForSend(events: TelemetryEvent[]): TelemetryEvent[][] {
const maxCount = Math.max(1, this.caps.maxEventsPerBatch);
const out: TelemetryEvent[][] = [];
for (let i = 0; i < events.length; i += maxCount) {
this.splitByBytes(events.slice(i, i + maxCount), out);
}
return out;
}
private splitByBytes(chunk: TelemetryEvent[], out: TelemetryEvent[][]): void {
if (chunk.length === 0) return;
const bytes = this.serializedBytes(this.buildEnvelope(chunk));
if (bytes <= this.caps.maxBodyBytes) {
out.push(chunk);
return;
}
if (chunk.length === 1) {
this.warn(
Number.isFinite(bytes)
? `dropping 1 event whose serialized envelope exceeds maxBodyBytes (${this.caps.maxBodyBytes} bytes); event="${chunk[0]?.name}"`
: `dropping 1 event with a non-serializable dimension (circular reference?); event="${chunk[0]?.name}"`,
);
return;
}
const mid = Math.ceil(chunk.length / 2);
this.splitByBytes(chunk.slice(0, mid), out);
this.splitByBytes(chunk.slice(mid), out);
}
private buildEnvelope(events: TelemetryEvent[], batchId?: string): TelemetryEventEnvelope {
const state = this.getState();
return {
app: this.config.app ?? "paperclip",
schemaVersion: this.config.schemaVersion ?? "1",
installId: state.installId,
version: this.version,View on GitHub (pinned to 120ae5428f)
Solutions
- Find the emit site for the named event and replace the raw object with a flat, explicitly-selected DTO.
- If the object must go through, run it through a JSON-safe replacer that drops circular keys before emit.
- Add a dev-mode assertion that JSON.stringify(event) succeeds for every emit.
- Prefer primitive fields (ids, names, counts) over nested objects in dimensions.
Example fix
// before
telemetry.emit({ name: 'task_inspected', dims: { task } });
// after
telemetry.emit({ name: 'task_inspected', dims: { taskId: task.id, status: task.status } }); Defensive patterns
Strategy: validation
Validate before calling
function isJsonSafe(value: unknown, seen = new WeakSet()): boolean {
if (value === null || typeof value !== 'object') return true;
if (seen.has(value as object)) return false;
seen.add(value as object);
return Object.values(value as object).every((v) => isJsonSafe(v, seen));
}
// before emit:
if (!isJsonSafe(event)) throw new Error(`Circular reference in telemetry event ${event.name}`); Type guard
function isSerializableTelemetryEvent(e: TelemetryEvent): boolean {
try {
return Number.isFinite(Buffer.byteLength(JSON.stringify(e)));
} catch {
return false;
}
} Prevention
- Never pass live domain objects into dimensions; map to flat DTOs at the emit boundary.
- Strip error.cause chains or replace with err.cause?.message.
- Assert serializability in dev builds so cycles fail fast before production drops them.
- Watch for this warn's telltale 'non-serializable dimension' phrasing during integration tests.
When it happens
Trigger: A telemetry dimension contains an object graph with a cycle: parent↔child associations, self-referential error.cause chains, ORM rows with relations, AST/scope objects captured wholesale.
Common situations: Passing live domain objects instead of DTOs; logging `error.cause` chains that loop; serializing request/response objects with back-references; reusing parsed structures that reference their container.
Related errors
- dropping 1 event whose serialized envelope exceeds maxBodyBy
- dropping batch ${batch.batchId} on terminal response (HTTP $
- dropping batch ${batch.batchId} after ${batch.attempt} attem
- pending-retry store full (bound=${bound}); evicted oldest ba
AI-assisted analysis of paperclipai/paperclip@120ae5428f (2026-08-18).
Data as JSON: /api/errors/5b48294583649479.
Report an issue: GitHub.