{"record":{"id":"8b7d8827964e9838","repo":"paperclipai/paperclip","slug":"paperclip-shapepaperclipworkspaceenvforexecution","errorCode":null,"errorMessage":"[paperclip] shapePaperclipWorkspaceEnvForExecution called with executionCwd=null on a remote target; stripping workspaceCwd to avoid leaking local paths into the remote environment.","messagePattern":"\\[paperclip\\] shapePaperclipWorkspaceEnvForExecution called with executionCwd=null on a remote target; stripping workspaceCwd to avoid leaking local paths into the remote environment\\.","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"packages/adapter-utils/src/server-utils.ts","lineNumber":3136,"sourceCode":"  const apiUrl =\n    process.env.PAPERCLIP_API_URL ??\n    process.env.PAPERCLIP_RUNTIME_API_URL ??\n    `http://${runtimeHost}:${runtimePort}`;\n  vars.PAPERCLIP_API_URL = apiUrl;\n  return vars;\n}\n\nexport function applyPaperclipWorkspaceEnv(\n  env: Record<string, string>,\n  input: {\n    workspaceCwd?: string | null;\n    workspaceSource?: string | null;\n    workspaceStrategy?: string | null;\n    workspaceId?: string | null;\n    workspaceRepoUrl?: string | null;\n    workspaceRepoRef?: string | null;\n    workspaceBranch?: string | null;\n    workspaceWorktreePath?: string | null;\n    agentHome?: string | null;\n  },\n): Record<string, string> {\n  const mappings = [\n    [\"PAPERCLIP_WORKSPACE_CWD\", input.workspaceCwd],\n    [\"PAPERCLIP_WORKSPACE_SOURCE\", input.workspaceSource],\n    [\"PAPERCLIP_WORKSPACE_STRATEGY\", input.workspaceStrategy],\n    [\"PAPERCLIP_WORKSPACE_ID\", input.workspaceId],\n    [\"PAPERCLIP_WORKSPACE_REPO_URL\", input.workspaceRepoUrl],\n    [\"PAPERCLIP_WORKSPACE_REPO_REF\", input.workspaceRepoRef],\n    [\"PAPERCLIP_WORKSPACE_BRANCH\", input.workspaceBranch],\n    [\"PAPERCLIP_WORKSPACE_WORKTREE_PATH\", input.workspaceWorktreePath],\n    [\"AGENT_HOME\", input.agentHome],\n  ] as const;\n\n  for (const [key, value] of mappings) {\n    if (typeof value === \"string\" && value.length > 0) {\n      env[key] = value;","sourceCodeStart":3118,"sourceCodeEnd":3154,"githubUrl":"https://github.com/paperclipai/paperclip/blob/3f1d897a7c018d76563a21c6e39c3c9b03933622/packages/adapter-utils/src/server-utils.ts#L3118-L3154","documentation":"shapePaperclipWorkspaceEnvForExecution rewrites workspace paths (workspaceCwd, hint cwds) so host-local paths never leak into a remote execution environment. Its contract: when executionTargetIsRemote is true, callers must pass a non-empty executionCwd (resolved via adapterExecutionTargetRemoteCwd, which always returns a non-empty string). When executionCwd is null/blank on a remote target, the helper strips workspaceCwd entirely and emits this warning so the contract violation is visible instead of silently leaking paths.","triggerScenarios":"An adapter calls the helper with executionTargetIsRemote: true but omits executionCwd or passes an empty/whitespace string — e.g. a caller that skipped adapterExecutionTargetRemoteCwd, or a new call site added without resolving the remote cwd first.","commonSituations":"New adapters or patched call sites forgetting the remote-cwd resolution step; executionCwd computed conditionally and the falsy branch slipping through; upgrading adapter-utils where the contract became enforced.","solutions":["In the caller, resolve the remote cwd first: const executionCwd = await adapterExecutionTargetRemoteCwd(...), and pass it as executionCwd.","Assert non-empty before calling: if (!executionCwd) throw — fail fast at the call site instead of accepting a degraded env.","Upgrade the adapter package if a released version has this bug and a fix exists.","Grep for call sites passing executionTargetIsRemote and audit each one's executionCwd provenance."],"exampleFix":"// before\nconst shaped = shapePaperclipWorkspaceEnvForExecution({\n  workspaceCwd,\n  workspaceHints,\n  executionTargetIsRemote: target.kind === \"remote\",\n  executionCwd: null, // warns: strips workspaceCwd\n});\n\n// after\nconst executionCwd = await adapterExecutionTargetRemoteCwd(target, workspace);\nif (!executionCwd) throw new Error(\"Failed to resolve remote execution cwd\");\nconst shaped = shapePaperclipWorkspaceEnvForExecution({\n  workspaceCwd,\n  workspaceHints,\n  executionTargetIsRemote: target.kind === \"remote\",\n  executionCwd,\n});","handlingStrategy":"validation","validationCode":"const executionCwd =\n  input.executionTargetIsRemote\n    ? await adapterExecutionTargetRemoteCwd(input.executionTarget)\n    : null;\nif (input.executionTargetIsRemote && !executionCwd) {\n  throw new Error(\"executionCwd must be resolved (non-empty) for remote targets\");\n}\nconst shaped = shapePaperclipWorkspaceEnvForExecution({ ...input, executionCwd });","typeGuard":"const isNonEmptyString = (v: unknown): v is string =>\n  typeof v === \"string\" && v.trim().length > 0;\n// gate the call: if (executionTargetIsRemote && !isNonEmptyString(executionCwd)) fail fast","tryCatchPattern":null,"preventionTips":["Always resolve executionCwd via adapterExecutionTargetRemoteCwd before shaping env for remote targets.","Fail fast at the call site when remote executionCwd is missing, instead of accepting the stripped-env degradation.","In adapter tests, assert no warning fires when executionTargetIsRemote is true (regression guard for the contract).","Audit every call site passing executionTargetIsRemote for executionCwd provenance after upgrading adapter-utils."],"tags":["adapter-utils","remote-execution","env-hygiene","contract-violation","path-leak"],"backgroundTag":"missing-execution-cwd","analyzedSha":"3f1d897a7c018d76563a21c6e39c3c9b03933622","analyzedAt":"2026-08-18T22:49:45.177Z","contentChangedAt":"2026-08-18T22:49:45.177Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}