github/copilot-sdk · error · ResponseError

cyclic_value

cyclic_value

Error message

${context.label} contains a cyclic reference at ${path}

What it means

The strict JSON validator detects cyclic references while walking journaled factory values, using an ancestors set. JSON.stringify throws on cycles ('Converting circular structure to JSON'), so the library rejects them eagerly with the precise path instead of letting serialization fail later and corrupt the journal.

Solutions

  1. Follow the error's `path` and break the cycle — remove the back-reference or store an id/weak reference instead.
  2. Serialize with a replacer that drops parent links, or map the structure to a plain JSON shape first.
  3. For needed relationships, journal ids and rehydrate the graph after replay instead of journaling object references.

Example fix

// before
node.parent = parent; // cycle when journaling node
return { node };
// after
return { node: { ...node, parent: parent ? parent.id : null } };
Defensive patterns

Strategy: validation

Validate before calling

function assertAcyclic(value, seen = new Set(), path = "$") {
  if (value && typeof value === "object") {
    if (seen.has(value)) throw new Error(`Cycle at ${path}`);
    seen.add(value);
    if (Array.isArray(value)) value.forEach((v, i) => assertAcyclic(v, seen, `${path}[${i}]`));
    else for (const [k, v] of Object.entries(value)) assertAcyclic(v, seen, `${path}.${k}`);
    seen.delete(value);
  }
}

Try / catch

try {
  session.runFactory(step);
} catch (e) {
  if (e?.details?.category === "cyclic_value") {
    console.error(`Break cyclic reference at ${e.details.path}`);
  } else throw e;
}

Prevention

When it happens

Trigger: Returning a factory result or step value that references itself directly (obj.self = obj) or indirectly (a.parent = a inside arrays/nested objects), e.g. attaching a parent back-reference for navigation.

Common situations: Doubly-linked tree nodes with parent pointers, DOM-like structures, caching an object inside itself, circular imports producing self-referencing module state.

Understand the failure class

Background: "JSON serialization failed", "not JSON serializable", "Failed to serialize": why JSON marshaling errors happen and how to fix them — this error's family across 46 libraries.

Related errors


AI-assisted analysis of github/copilot-sdk@cd8cf15dc3 (2026-09-09). Data as JSON: /api/errors/867124b1dedeb747. Report an issue: GitHub.

Appendix: source

Thrown at nodejs/src/session.ts:2351

            typeof current === "bigint"
        ) {
            throw strictJsonValidationError(
                context,
                "unsupported_type",
                `${context.label} contains a function, symbol, or BigInt at ${path}`,
                path
            );
        }
        if (typeof current !== "object") {
            throw strictJsonValidationError(
                context,
                "unsupported_type",
                `${context.label} contains a function, symbol, or BigInt at ${path}`,
                path
            );
        }
        if (ancestors.has(current)) {
            throw strictJsonValidationError(
                context,
                "cyclic_value",
                `${context.label} contains a cyclic reference at ${path}`,
                path
            );
        }

        ancestors.add(current);
        try {
            if (Array.isArray(current)) {
                const keys = Reflect.ownKeys(current);
                if (
                    keys.length !== current.length + 1 ||
                    keys.some(
                        (key) =>
                            key !== "length" &&
                            (typeof key !== "string" ||
                                !/^(0|[1-9]\d*)$/.test(key) ||

View on GitHub (pinned to cd8cf15dc3)