paperclipai/paperclip · error · Error
Circular workspace dependency while staging ${packageName}.
Error message
Circular workspace dependency while staging ${packageName}. What it means
Thrown by resolveGitInstallWorkspacePackages (install.ts:65) when the depth-first visit over @paperclipai/* workspace dependencies re-enters a package already on the current visit stack. Detects a dependency cycle in the pnpm workspace graph during a git-based (source checkout) install.
Source
Thrown at cli/src/commands/install.ts:65
const stderr = error && typeof error === "object" && "stderr" in error && typeof error.stderr === "string"
? error.stderr.trim()
: "";
if (!stderr || (error instanceof Error && error.message.includes(stderr))) throw error;
throw new Error(`${error instanceof Error ? error.message : String(error)}\n${stderr}`, { cause: error });
}
}
export function resolveGitInstallWorkspacePackages(checkoutPath: string): ReleasePackageEntry[] {
const manifestPath = path.join(checkoutPath, "scripts", "release-package-manifest.json");
const manifest = JSON.parse(fs.readFileSync(manifestPath, "utf8")) as ReleasePackageEntry[];
const packageByName = new Map(manifest.map((entry) => [entry.name, entry]));
const visiting = new Set<string>();
const visited = new Set<string>();
const ordered: ReleasePackageEntry[] = [];
const visit = (packageName: string): void => {
if (visited.has(packageName)) return;
if (visiting.has(packageName)) throw new Error(`Circular workspace dependency while staging ${packageName}.`);
const entry = packageByName.get(packageName);
if (!entry) throw new Error(`Git install cannot stage workspace dependency ${packageName}; it is missing from scripts/release-package-manifest.json.`);
visiting.add(packageName);
const packageJson = JSON.parse(fs.readFileSync(path.join(checkoutPath, entry.dir, "package.json"), "utf8")) as Record<string, unknown>;
for (const section of ["dependencies", "optionalDependencies", "peerDependencies"] as const) {
const dependencies = packageJson[section];
if (!dependencies || typeof dependencies !== "object") continue;
for (const dependencyName of Object.keys(dependencies)) {
if (dependencyName.startsWith("@paperclipai/")) visit(dependencyName);
}
}
visiting.delete(packageName);
visited.add(packageName);
ordered.push(entry);
};
visit("@paperclipai/server");
return ordered;View on GitHub (pinned to 67001ec6eb)
Solutions
- Break the cycle in the workspace: move shared code into a third package that both depend on.
- If the cycle is in optionalDependencies/peerDependencies, re-evaluate whether it is truly needed.
- Re-run the install after fixing the graph; verify with `pnpm why @paperclipai/<pkg>`.
Example fix
// before: packages/a package.json
dependencies: { "@paperclipai/b": "workspace:*" }
// packages/b package.json
dependencies: { "@paperclipai/a": "workspace:*" }
// after: extract shared code into @paperclipai/shared and have both depend on it Defensive patterns
Strategy: validation
Validate before calling
function detectWorkspaceCycle(manifest: ReleasePackageEntry[], checkoutPath: string): string | null {
const byName = new Map(manifest.map((e) => [e.name, e]));
const visit = (name: string, stack: string[]): string[] | null => {
if (stack.includes(name)) return [...stack, name];
const entry = byName.get(name);
if (!entry) return null;
const pkg = JSON.parse(fs.readFileSync(path.join(checkoutPath, entry.dir, 'package.json'), 'utf8'));
for (const section of ['dependencies', 'optionalDependencies', 'peerDependencies']) {
const deps = pkg[section];
if (!deps) continue;
for (const dep of Object.keys(deps)) {
if (dep.startsWith('@paperclipai/')) {
const cycle = visit(dep, [...stack, name]);
if (cycle) return cycle;
}
}
}
return null;
};
const cycle = visit('@paperclipai/server', []);
return cycle ? `Cycle: ${cycle.join(' -> ')}` : null;
} Try / catch
try {
const ordered = resolveGitInstallWorkspacePackages(checkoutPath);
} catch (err) {
if (err instanceof Error && err.message.startsWith('Circular workspace dependency')) {
console.error('A workspace dependency cycle was introduced; break it before installing.');
}
throw err;
} Prevention
- Keep the @paperclipai workspace graph acyclic; extract shared code into leaf packages.
- Run a cycle-detection check in CI on the workspace manifest.
- Review dependency direction before adding @paperclipai/* cross-references.
When it happens
Trigger: Two @paperclipai packages mutually depend on each other (A depends on B, B depends on A), or a longer cycle, while staging workspace packages in topological order. Triggered during `paperclipai install --ref ...` from a git checkout.
Common situations: A recent change added a circular workspace dependency; the manifest-driven installer cannot order such a graph.
Related errors
- Git install cannot stage workspace dependency ${packageName}
- --version is only supported for npm package installs, not lo
- Approval request failed.
- Invalid --secret-value "${raw}". Use key=value.
- API error ${response.status}: ${bytes.toString("utf8")}
AI-assisted analysis of paperclipai/paperclip@67001ec6eb (2026-08-12).
Data as JSON: /api/errors/9af244b872bbfe14.
Report an issue: GitHub.