paperclipai/paperclip · error · Error

Registered base project workspace Paperclip config at ${conf

Error message

Registered base project workspace Paperclip config at ${configPath} cannot be inspected (${errorCode(error)}${detail ?? ""}).

What it means

Thrown by inspectDeclaredEntry in worktree-seed-source.ts when lstatSync on the declared config path (or the .paperclip directory entry) fails with any code other than ENOENT. Absent entries are fine (returns null); unreadable, malformed, or looping entries (EACCES, EPERM, ENOTDIR, ELOOP, EMFILE) mean the declared source cannot be verified, and the resolver fails closed rather than silently seeding from a different instance.

Source

Thrown at packages/shared/src/worktree-seed-source.ts:62

  throw new Error(`Registered ${label} Paperclip config has no PAPERCLIP_INSTANCE_ID binding.`);
}

function errorCode(error: unknown): string {
  return (error as NodeJS.ErrnoException | null)?.code ?? "unknown error";
}

/**
 * Inspect a directory entry without following it, returning null only when it is absent.
 *
 * Any other failure means the declared path is unreadable or malformed, and a guess there
 * would silently seed from a different instance.
 */
function inspectDeclaredEntry(entryPath: string, configPath: string, detail?: string): Stats | null {
  try {
    return lstatSync(entryPath);
  } catch (error) {
    if (errorCode(error) === "ENOENT") return null;
    throw new Error(
      `Registered base project workspace Paperclip config at ${configPath} cannot be inspected (${errorCode(error)}${detail ?? ""}).`,
    );
  }
}

/**
 * Whether a base project workspace declares an instance config of its own.
 *
 * This tests directory entries and does not follow them. A dangling or aliased symlink,
 * at the config itself or at the `.paperclip` directory holding it, still counts as a
 * declared config, so the resolver rejects the malformed source instead of falling back
 * to another one.
 */
export function baseWorkspaceDeclaresInstanceConfig(baseWorkspaceCwd: string): boolean {
  const configDir = path.join(baseWorkspaceCwd, ".paperclip");
  const configPath = path.join(configDir, "config.json");
  if (inspectDeclaredEntry(configPath, configPath)) return true;

View on GitHub (pinned to a7e689b3c3)

Solutions

  1. Read the errno in the message: EACCES/EPERM -> fix ownership/permissions on the workspace and every parent (chmod/chown so the process can lstat); ENOTDIR -> a path component is a file, correct the layout; ELOOP -> remove the symlink loop on .paperclip or the config; EMFILE -> raise the FD limit.
  2. Verify with: ls -la <workspace>/.paperclip and namei -l <workspace>/.paperclip/config.json to see where traversal breaks.
  3. Run the command as the user that owns the workspace, or grant that user read+execute (search) permission on the path.
  4. Replace a symlinked .paperclip with a real directory if the link target is missing or cyclic.

Example fix

# before: .paperclip is a symlink loop
ln -s .paperclip .paperclip  # accidental self-link

# after
rm <workspace>/.paperclip && mkdir <workspace>/.paperclip
# then re-create or restore config.json inside it
Defensive patterns

Strategy: validation

Validate before calling

import fs from "node:fs";
function workspaceInspectable(workspace: string): boolean {
  const entries = [workspace, path.join(workspace, ".paperclip"), path.join(workspace, ".paperclip", "config.json")];
  try {
    entries.forEach((p) => fs.lstatSync(p));
    return true;
  } catch {
    return false;
  }
}

Try / catch

try {
  resolveRegisteredWorktreeSeedSource(input);
} catch (error) {
  if (error instanceof Error && error.message.includes("cannot be inspected")) {
    // parse the errno in the message: EACCES -> fix perms/owner; ENOTDIR/ELOOP -> fix layout/symlink; EMFILE -> raise FD limit
  }
  throw error;
}

Prevention

When it happens

Trigger: resolveRegisteredWorktreeSeedSource probes <baseWorkspace>/.paperclip/config.json or the .paperclip entry: a permission-denied home/workspace directory yields EACCES; a path component that is a file yields ENOTDIR; a symlink cycle on .paperclip yields ELOOP; FD exhaustion yields EMFILE.

Common situations: Workspaces under directories owned by another user or with restrictive modes (e.g. after chown/restore from archive); .paperclip accidentally created as a file, or a parent of it; self-referential symlinks left by broken dotfile managers; running as a different user/service account than the one that created the workspace.

Related errors


AI-assisted analysis of paperclipai/paperclip@a7e689b3c3 (2026-08-21). Data as JSON: /api/errors/c4518ef6904ced2b. Report an issue: GitHub.