abhigyanpatwari/GitNexus · warning
[cobol-copy-expander] Circular COPY detected: ${cs.target} (
Error message
[cobol-copy-expander] Circular COPY detected: ${cs.target} (${resolvedPath}) includes itself. Skipping expansion. What it means
During COPY expansion, a copybook resolved to a path already on the active expansion chain (the visited set) — it includes itself, directly or transitively (A COPYs B, B COPYs A). Expansion of that COPY is skipped and the original lines kept; the warning fires once per path per run via the warnedCircular set.
Source
Thrown at gitnexus/src/core/ingestion/cobol/cobol-copy-expander.ts:458
// Record resolution metadata
allResolutions.push({
copyTarget: cs.target,
resolvedPath,
line: cs.startLine,
replacing: cs.replacing,
library: cs.library,
});
// Cannot resolve — keep original lines
if (resolvedPath === null) {
continue;
}
// Cycle detection
if (visited.has(resolvedPath)) {
if (!warnedCircular.has(resolvedPath)) {
warnedCircular.add(resolvedPath);
logger.warn(
`[cobol-copy-expander] Circular COPY detected: ${cs.target} (${resolvedPath}) ` +
`includes itself. Skipping expansion.`,
);
}
continue;
}
// Max depth exceeded — keep unexpanded
if (depth >= maxDepth) {
logger.warn(
`[cobol-copy-expander] Max expansion depth (${maxDepth}) reached for ` +
`COPY ${cs.target} in ${srcPath}. Skipping expansion.`,
);
continue;
}
// Guard against exponential breadth amplification (N copybooks each with N COPYs)
if (++totalExpansions > MAX_TOTAL_EXPANSIONS) {View on GitHub (pinned to aac7515d2a)
Solutions
- Break the cycle: remove the recursive COPY statement from one of the copybooks
- Extract the shared fragment into a third, leaf copybook that both A and B COPY
- Re-run analysis — the expander keeps original lines so output stays parseable, but verify results for those members
Example fix
* before — CPYB contains: COPY CPYA. CPYA contains: COPY CPYB.
* after — remove the back-reference from CPYB
IDENTIFICATION DIVISION.
... Defensive patterns
Strategy: validation
Validate before calling
// Pre-flight: DFS the copybook graph and fail on cycles before analysis
function findCopyCycle(adj: Map<string, string[]>): string[] | null {
const state = new Map<string, 'visiting' | 'done'>();
const dfs = (node: string, stack: string[]): string[] | null => {
state.set(node, 'visiting');
for (const next of adj.get(node) ?? []) {
if (state.get(next) === 'visiting') return [...stack, next];
if (!state.has(next)) {
const cycle = dfs(next, [...stack, next]);
if (cycle) return cycle;
}
}
state.set(node, 'done');
return null;
};
for (const node of adj.keys()) if (!state.has(node)) {
const cycle = dfs(node, [node]);
if (cycle) return cycle;
}
return null;
} Type guard
function isAcyclic(adj: Map<string, string[]>): boolean {
return findCopyCycle(adj) === null;
} Prevention
- Never let a copybook (directly or transitively) COPY anything on its own expansion chain
- Run a cycle check over the copybook graph in CI for mainframe source trees
- Compile members with a real COBOL compiler periodically — it rejects circular COPYs
When it happens
Trigger: A copybook chain that loops: copybook A contains COPY B while B contains COPY A, or a copybook COPYs itself.
Common situations: Refactored copybooks that accidentally reference each other; copy-paste introducing a self-COPY. Real COBOL compilers reject such cycles, so this usually surfaces on legacy members that were never compiled as-is.
Related errors
- [cobol-copy-expander] Max expansion depth (${maxDepth}) reac
- [cobol-copy-expander] Max total expansions (${MAX_TOTAL_EXPA
- [cobol-processor] Skipping oversized file (${(file.content.l
AI-assisted analysis of abhigyanpatwari/GitNexus@aac7515d2a (2026-08-20).
Data as JSON: /api/errors/b955fd297f8d1ba5.
Report an issue: GitHub.