paperclipai/paperclip · warning

[paperclip] shapePaperclipWorkspaceEnvForExecution called…

Error message

[paperclip] shapePaperclipWorkspaceEnvForExecution called with executionCwd=null on a remote target; stripping workspaceCwd to avoid leaking local paths into the remote environment.

What it means

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.

Solutions

  1. In the caller, resolve the remote cwd first: const executionCwd = await adapterExecutionTargetRemoteCwd(...), and pass it as executionCwd.
  2. Assert non-empty before calling: if (!executionCwd) throw — fail fast at the call site instead of accepting a degraded env.
  3. Upgrade the adapter package if a released version has this bug and a fix exists.
  4. Grep for call sites passing executionTargetIsRemote and audit each one's executionCwd provenance.

Example fix

// before
const shaped = shapePaperclipWorkspaceEnvForExecution({
  workspaceCwd,
  workspaceHints,
  executionTargetIsRemote: target.kind === "remote",
  executionCwd: null, // warns: strips workspaceCwd
});

// after
const executionCwd = await adapterExecutionTargetRemoteCwd(target, workspace);
if (!executionCwd) throw new Error("Failed to resolve remote execution cwd");
const shaped = shapePaperclipWorkspaceEnvForExecution({
  workspaceCwd,
  workspaceHints,
  executionTargetIsRemote: target.kind === "remote",
  executionCwd,
});
Defensive patterns

Strategy: validation

Validate before calling

const executionCwd =
  input.executionTargetIsRemote
    ? await adapterExecutionTargetRemoteCwd(input.executionTarget)
    : null;
if (input.executionTargetIsRemote && !executionCwd) {
  throw new Error("executionCwd must be resolved (non-empty) for remote targets");
}
const shaped = shapePaperclipWorkspaceEnvForExecution({ ...input, executionCwd });

Type guard

const isNonEmptyString = (v: unknown): v is string =>
  typeof v === "string" && v.trim().length > 0;
// gate the call: if (executionTargetIsRemote && !isNonEmptyString(executionCwd)) fail fast

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of paperclipai/paperclip@3f1d897a7c (2026-08-18). Data as JSON: /api/errors/8b7d8827964e9838. Report an issue: GitHub.

Appendix: source

Thrown at packages/adapter-utils/src/server-utils.ts:3136

  const apiUrl =
    process.env.PAPERCLIP_API_URL ??
    process.env.PAPERCLIP_RUNTIME_API_URL ??
    `http://${runtimeHost}:${runtimePort}`;
  vars.PAPERCLIP_API_URL = apiUrl;
  return vars;
}

export function applyPaperclipWorkspaceEnv(
  env: Record<string, string>,
  input: {
    workspaceCwd?: string | null;
    workspaceSource?: string | null;
    workspaceStrategy?: string | null;
    workspaceId?: string | null;
    workspaceRepoUrl?: string | null;
    workspaceRepoRef?: string | null;
    workspaceBranch?: string | null;
    workspaceWorktreePath?: string | null;
    agentHome?: string | null;
  },
): Record<string, string> {
  const mappings = [
    ["PAPERCLIP_WORKSPACE_CWD", input.workspaceCwd],
    ["PAPERCLIP_WORKSPACE_SOURCE", input.workspaceSource],
    ["PAPERCLIP_WORKSPACE_STRATEGY", input.workspaceStrategy],
    ["PAPERCLIP_WORKSPACE_ID", input.workspaceId],
    ["PAPERCLIP_WORKSPACE_REPO_URL", input.workspaceRepoUrl],
    ["PAPERCLIP_WORKSPACE_REPO_REF", input.workspaceRepoRef],
    ["PAPERCLIP_WORKSPACE_BRANCH", input.workspaceBranch],
    ["PAPERCLIP_WORKSPACE_WORKTREE_PATH", input.workspaceWorktreePath],
    ["AGENT_HOME", input.agentHome],
  ] as const;

  for (const [key, value] of mappings) {
    if (typeof value === "string" && value.length > 0) {
      env[key] = value;

View on GitHub (pinned to 3f1d897a7c)