paperclipai/paperclip · error
additional source projectId is not a simple path segment: ${
Error message
additional source projectId is not a simple path segment: ${projectId} What it means
Thrown during additional-source processing when projectId is empty, or contains a slash, backslash, or '..'. The projectId is used directly in a label and joined into a remote directory path (runtimeRootDir/project-<id>), so it must be a single safe path segment; otherwise it would enable path injection or directory ambiguity. Caught per-source so one bad project does not abort the run.
Source
Thrown at packages/adapter-utils/src/sandbox-managed-runtime.ts:1019
// their own roots. No workspace, git-history, or `.paperclip-runtime`
// semantics apply; those stay anchor-only. Per-project failure isolation: one
// project's confinement or sync failure logs a warning and is skipped, and
// the run plus the other projects continue. Only a project that stages
// successfully appears in `additionalSourceDirs`.
for (const source of input.additionalSources ?? []) {
const { localPath, projectId } = source;
const label = `project-${projectId}`;
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 remoteProjectDir = path.posix.join(runtimeRootDir, label);
await emitRuntimeStatus(input.onRuntimeProgress, "config_sync", "Syncing referenced project to sandbox");
await stageConfinedSyncIn({
files: [{
sourcePath: localPath,
targetPath: remoteProjectDir,
kind: "directory",
exclude: additionalSourceExclude,
access: "ro",
}],
sourceRoots: [localPath],
targetRoots: [remoteProjectDir],
progressLabel: label,
statusPhase: "config_sync",
progressBytes: 0,
});
additionalSourceDirs[projectId] = remoteProjectDir;View on GitHub (pinned to 67001ec6eb)
Solutions
- Generate projectId from a safe charset (alphanumeric, dash, underscore) — e.g. slugify the repo name.
- Validate projectId with /^[A-Za-z0-9_-]+$/ at the collection boundary before passing to prepareSandboxManagedRuntime.
- For org/repo inputs, join with a non-separator like '-' (e.g. 'org-repo') rather than '/'.
- Ensure projectId is always populated (non-empty) for referenced projects.
Example fix
// before
additionalSources: [{ localPath: abs, projectId: 'acme/widgets' }]
// after
additionalSources: [{ localPath: abs, projectId: 'acme-widgets' }] Defensive patterns
Strategy: validation
Validate before calling
const PROJECT_ID_RE = /^[A-Za-z0-9_-]+$/;
for (const s of additionalSources ?? []) {
if (!PROJECT_ID_RE.test(s.projectId)) {
throw new Error(`additionalSource projectId must be a safe segment: ${s.projectId}`);
}
} Type guard
function isSafeProjectId(id: string): boolean {
return typeof id === 'string' && id.length > 0 && /^[A-Za-z0-9_-]+$/.test(id);
} Prevention
- Slugify any org/repo input into [A-Za-z0-9_-] before using it as projectId.
- Make projectId a required field in the additional-source schema.
- Reject empty/slash/backslash ids at the API boundary.
When it happens
Trigger: An additionalSources entry with projectId = '', 'a/b', 'a\b', or '..'. The check also rejects backslashes to keep POSIX remote paths safe.
Common situations: projectId sourced from an unvalidated user input or repo name with a slash; a UUID-style id that accidentally includes a path separator; a default empty string when the field was optional and not set; a multi-segment org/repo slug used verbatim.
Related errors
- networkAllowlist[${index}] must use an exact hostname; wildc
- sync operation ${label} path is not a confined absolute path
- sync operation ${label} path escapes its confinement root: $
- provision stageFile.name must be a simple basename, got: ${s
- additional source localPath is not an absolute path: ${local
AI-assisted analysis of paperclipai/paperclip@67001ec6eb (2026-08-12).
Data as JSON: /api/errors/590610b72c0342cb.
Report an issue: GitHub.