danny-avila/LibreChat · error · AgentRunEnvelopeError
${path} contains symbol keys
Error message
${path} contains symbol keys What it means
Thrown by cloneJsonValue when an object or array in the payload has one or more own symbol-keyed properties (Object.getOwnPropertySymbols returns a non-empty list). JSON only has string keys, so symbol-keyed properties are silently dropped by JSON.stringify; the envelope clone rejects them outright to make the loss explicit. This catches hidden 'tag' symbols that libraries attach to mark objects (e.g. React $$typeof, mongoose symbols).
Source
Thrown at packages/api/src/agents/envelope.ts:131
throw new AgentRunEnvelopeError(`${path} must contain only finite numbers`);
}
return value;
}
if (typeof value !== 'object') {
throw new AgentRunEnvelopeError(`${path} contains a non-JSON ${typeof value} value`);
}
if (ancestors.has(value)) {
throw new AgentRunEnvelopeError(`${path} contains a circular reference`);
}
ancestors.add(value);
try {
const symbolKeys = Object.getOwnPropertySymbols(value);
if (symbolKeys.length > 0) {
throw new AgentRunEnvelopeError(`${path} contains symbol keys`);
}
if (Array.isArray(value)) {
const cloned: unknown[] = new Array(value.length);
let clonedItemCount = 0;
for (const key of Object.getOwnPropertyNames(value)) {
if (key === 'length') {
continue;
}
const index = Number(key);
if (
!Number.isSafeInteger(index) ||
index < 0 ||
index >= value.length ||
String(index) !== key
) {
throw new AgentRunEnvelopeError(`${path} contains non-index array properties`);
}View on GitHub (pinned to 5ff282f900)
Solutions
- Map the object to a plain literal with only the fields you need (pick/serialize) before placing it in the payload.
- Call Object.getOwnPropertySymbols(obj).forEach(s => delete obj[s]) if you intentionally want to strip them (only when safe).
- Construct payload objects from explicit field lists rather than spreading SDK/ORM return values.
Example fix
// before
const doc = await Model.findById(id).lean(); // may carry mongoose symbols
createAgentRunEnvelope({ protocol, requestId, receivedAt, principal, payload: { record: doc } });
// after
const doc = await Model.findById(id).lean();
const record = JSON.parse(JSON.stringify(doc)); // or explicit pick
createAgentRunEnvelope({ protocol, requestId, receivedAt, principal, payload: { record } }); Defensive patterns
Strategy: type-guard
Validate before calling
function hasSymbolKeys(value: unknown): boolean {
return typeof value === 'object' && value !== null && Object.getOwnPropertySymbols(value).length > 0;
}
function stripSymbolsDeep(obj: unknown): unknown {
if (Array.isArray(obj)) return obj.map(stripSymbolsDeep);
if (obj && typeof obj === 'object') {
const clean: Record<string, unknown> = {};
for (const [k, v] of Object.entries(obj)) clean[k] = stripSymbolsDeep(v);
return clean;
}
return obj;
} Type guard
function isSymbolFree(value: unknown, seen = new WeakSet()): boolean {
if (typeof value !== 'object' || value === null) return true;
if (seen.has(value as object)) return true; // cycle handled elsewhere
seen.add(value as object);
if (Object.getOwnPropertySymbols(value).length > 0) return false;
const vals = Array.isArray(value) ? value : Object.values(value);
return vals.every((v) => isSymbolFree(v, seen));
} Try / catch
try {
const env = createAgentRunEnvelope(input);
} catch (e) {
if (e instanceof AgentRunEnvelopeError && /symbol keys/.test(e.message)) {
input.payload = JSON.parse(JSON.stringify(input.payload)) as typeof input.payload;
// retry — JSON round-trip drops symbol keys
} else throw e;
} Prevention
- Convert ORM/SDK objects to plain DTOs before placing them in the payload.
- In tests, assert Object.getOwnPropertySymbols(payload).length === 0 for representative payloads.
- Avoid the Symbol-as-private-field convention inside objects destined for the envelope.
When it happens
Trigger: Passing a Mongoose document, a React element, a styled-components object, or any value that has been processed by a library that tags objects with Symbol() keys. Also a manually set obj[Symbol('flag')] = true.
Common situations: Passing a raw ORM/document object straight into the envelope instead of a plain DTO; spreading a library-annotated object into the payload; using a Symbol as a private field convention.
Related errors
- ${path} contains a non-JSON ${typeof value} value
- ${path} contains a circular reference
- ${path} contains non-index array properties
- ${path}[${index}] must not be an accessor property
- ${path} contains sparse array entries
AI-assisted analysis of danny-avila/LibreChat@5ff282f900 (2026-08-12).
Data as JSON: /api/errors/6fffd767cf0f3574.
Report an issue: GitHub.