danny-avila/LibreChat · error · AgentRunEnvelopeError
${path}.${key} must be an enumerable property
Error message
${path}.${key} must be an enumerable property What it means
Thrown by cloneJsonValue when a plain object's own property descriptor has enumerable !== true. Object.getOwnPropertyNames returns non-enumerable keys too, so properties defined with enumerable:false (or hidden flags) are caught. JSON.stringify skips non-enumerable keys, which would silently drop data; the envelope clone surfaces the loss as an error.
Source
Thrown at packages/api/src/agents/envelope.ts:174
clonedItemCount++;
}
if (clonedItemCount !== value.length) {
throw new AgentRunEnvelopeError(`${path} contains sparse array entries`);
}
return cloned;
}
const prototype = Object.getPrototypeOf(value);
if (prototype !== Object.prototype && prototype !== null) {
const typeName = value.constructor?.name ?? 'object';
throw new AgentRunEnvelopeError(`${path} contains a non-plain ${typeName} value`);
}
const cloned: { [key: string]: unknown } = {};
for (const key of Object.getOwnPropertyNames(value)) {
const descriptor = Object.getOwnPropertyDescriptor(value, key);
if (descriptor?.enumerable !== true) {
throw new AgentRunEnvelopeError(`${path}.${key} must be an enumerable property`);
}
if (!Object.prototype.hasOwnProperty.call(descriptor, 'value')) {
throw new AgentRunEnvelopeError(`${path}.${key} must not be an accessor property`);
}
const propertyValue: unknown = descriptor.value;
Object.defineProperty(cloned, key, {
configurable: true,
enumerable: true,
writable: true,
value: cloneJsonValue(propertyValue, `${path}.${key}`, ancestors, depth + 1),
});
}
return cloned;
} finally {
ancestors.delete(value);
}
}
View on GitHub (pinned to 5ff282f900)
Solutions
- Recreate the object as a literal containing only the fields you need, copied by name.
- Redefine the property as enumerable: Object.defineProperty(obj, key, { enumerable: true }).
- Use Object.entries / a manual pick list (which only see enumerable props) to rebuild a clean plain object.
Example fix
// before
Object.defineProperty(cfg, 'secret', { value: 'x', enumerable: false });
createAgentRunEnvelope({ protocol, requestId, receivedAt, principal, payload: { cfg } });
// after
const cfg = { ...{ secret: 'x' } }; // literal, enumerable by default
createAgentRunEnvelope({ protocol, requestId, receivedAt, principal, payload: { cfg } }); Defensive patterns
Strategy: validation
Validate before calling
function allOwnPropsEnumerable(obj: Record<string, unknown>): boolean {
return Object.getOwnPropertyNames(obj).every((k) =>
Object.getOwnPropertyDescriptor(obj, k)?.enumerable === true);
}
function toEnumerablePlain<T extends Record<string, unknown>>(obj: T): Record<string, unknown> {
const out: Record<string, unknown> = {};
for (const [k, v] of Object.entries(obj)) out[k] = v; // entries sees only enumerable
return out;
} Type guard
function isAllEnumerable(obj: Record<PropertyKey, unknown>): boolean {
return Object.getOwnPropertyNames(obj).every(
(k) => Object.getOwnPropertyDescriptor(obj, k)?.enumerable === true,
);
} Try / catch
try {
const env = createAgentRunEnvelope(input);
} catch (e) {
if (e instanceof AgentRunEnvelopeError && /enumerable property/.test(e.message)) {
input.payload = JSON.parse(JSON.stringify(input.payload)) as typeof input.payload;
// JSON round-trip drops non-enumerable keys; confirm that is acceptable
} else throw e;
} Prevention
- Avoid Object.defineProperty with enumerable:false on payload-bound objects.
- Build payload objects as literals; do not reuse frozen/hidden-flag templates.
- Test that Object.keys(payload) returns every field you intend to send.
When it happens
Trigger: Object.defineProperty(obj, 'id', { value: 1, enumerable: false }), or libraries that define config fields as non-enumerable. Also class fields declared as non-enumerable via decorators.
Common situations: Using defineProperty for 'private' fields that you later need in the payload; objects returned from Object.assign over a frozen/non-enumerable template; ORM hidden columns flagged non-enumerable.
Related errors
- ${path}.${key} must not be an accessor property
- ${path} contains a non-JSON ${typeof value} value
- ${path} contains a circular reference
- ${path} contains symbol keys
- ${path} contains non-index array properties
AI-assisted analysis of danny-avila/LibreChat@5ff282f900 (2026-08-12).
Data as JSON: /api/errors/ff407a1345b84b00.
Report an issue: GitHub.