paperclipai/paperclip · warning

additional source localPath is not an absolute path: ${local

Error message

additional source localPath is not an absolute path: ${localPath}

What it means

Thrown inside the additional-sources staging loop in remote-managed-runtime when path.posix.isAbsolute(localPath) is false. Additional sources are synced to the remote runtime via rsync-over-ssh and require a posix-absolute local path. The throw is caught per-source and logged as a warning (the source is skipped); it does not fail the whole run.

Source

Thrown at packages/adapter-utils/src/remote-managed-runtime.ts:185

        baselineSnapshot,
        restoreGitHistory: preparedWorkspace.gitBacked,
        onProgress: input.onProgress,
      });
    }
    throw error;
  }

  // Stage each referenced (additional) project as a plain, read-only tree in its
  // OWN isolated remote directory (`project-<projectId>`). Additional sources
  // never get the anchor's git-history/overlay semantics. Per-project failure
  // isolation: one project's failure logs a warning and is skipped; the run and
  // the other projects continue (no workspace restore, unlike an asset failure).
  const additionalSourceDirs: Record<string, string> = {};
  for (const source of input.additionalSources ?? []) {
    const { localPath, projectId } = source;
    try {
      if (!path.posix.isAbsolute(localPath)) {
        throw new Error(`additional source localPath is not an absolute path: ${localPath}`);
      }
      if (
        projectId.length === 0 ||
        projectId.includes("/") ||
        projectId.includes("\\") ||
        projectId.includes("..")
      ) {
        throw new Error(`additional source projectId is not a simple path segment: ${projectId}`);
      }
      const remoteDir = path.posix.join(runtimeRootDir, `project-${projectId}`);
      await syncDirectoryToSsh({
        spec: input.spec,
        localDir: localPath,
        remoteDir,
        exclude: REMOTE_ADDITIONAL_SOURCE_HEAVY_DIR_EXCLUDES,
        onProgress: input.onProgress,
        progressLabel: `project-${projectId}`,
      });

View on GitHub (pinned to 67001ec6eb)

Solutions

  1. Resolve localPath to an absolute path before constructing additionalSources: path.resolve(baseDir, source.localPath).
  2. Watch the warning logs ([paperclip] Failed to stage referenced project ...) — a skipped source is reported there, not as a thrown error to your caller.
  3. Validate at config load: assert(path.isAbsolute(source.localPath)) for every additional source.
  4. If you need to keep relative paths in config, resolve them at the boundary that knows the base directory before handing them to the runtime.

Example fix

// before
additionalSources: [{ localPath: "./libs/foo", projectId: "foo" }]

// after
additionalSources: [{ localPath: path.resolve(baseDir, "libs/foo"), projectId: "foo" }]
Defensive patterns

Strategy: validation

Validate before calling

function normalizeAdditionalSource(source: { localPath: string; projectId: string }, baseDir: string) {
  const localPath = path.isAbsolute(source.localPath) ? source.localPath : path.resolve(baseDir, source.localPath);
  if (!path.isAbsolute(localPath)) {
    throw new Error(`additional source localPath could not be resolved to absolute: ${source.localPath}`);
  }
  return { ...source, localPath };
}

const normalized = (input.additionalSources ?? []).map((s) => normalizeAdditionalSource(s, baseDir));

Type guard

function isAdditionalSource(value: unknown): value is { localPath: string; projectId: string } {
  return (
    typeof value === "object" && value !== null &&
    typeof (value as any).localPath === "string" &&
    typeof (value as any).projectId === "string"
  );
}

Try / catch

// Per-source failures are already caught and logged as warnings by remote-managed-runtime.
// Watch for `[paperclip] Failed to stage referenced project` in logs to catch silent skips.
for (const source of input.additionalSources ?? []) {
  if (!path.isAbsolute(source.localPath)) {
    console.warn(`Skipping non-absolute additional source: ${source.localPath}`);
  }
}

Prevention

When it happens

Trigger: additionalSources: [{ localPath: "./libs/foo", projectId: "foo" }] or [{ localPath: "libs/foo", projectId: "foo" }] — relative paths hit the check at remote-managed-runtime.ts:184-186. The throw is wrapped in a try/catch at line 183/205 that logs `[paperclip] Failed to stage referenced project ${projectId}; skipping it.` and continues with the next source.

Common situations: Config that stores project paths relative to a base directory expecting the runtime to resolve them; user input from a CLI that did not call path.resolve; or symlinked paths whose toString is relative. The result is a missing project on the remote rather than a hard failure, which can mask itself as a downstream import error.

Related errors


AI-assisted analysis of paperclipai/paperclip@67001ec6eb (2026-08-12). Data as JSON: /api/errors/0825f25a82d1d272. Report an issue: GitHub.