paperclipai/paperclip · error · Error

Invalid GitHub launcher run ID

Error message

Invalid GitHub launcher run ID

What it means

githubOperationLauncherDirectory builds the filesystem path for a managed GitHub operation launcher directory but only permits controller-generated run IDs, enforced by the regex /^[a-zA-Z0-9_-]+$/. This guard prevents arbitrary strings (potentially from untrusted run identifiers) from escaping into path construction. An ID containing slashes, dots, spaces, or other characters triggers this error.

Source

Thrown at packages/adapter-utils/src/execution-target.ts:1529

    restoreWorkspace: prepared.restoreWorkspace,
  };
}

export function runtimeAssetDir(
  prepared: Pick<PreparedAdapterExecutionTargetRuntime, "assetDirs">,
  key: string,
  fallbackRemoteCwd: string,
): string {
  return prepared.assetDirs[key] ?? path.posix.join(fallbackRemoteCwd, ".paperclip-runtime", key);
}

type GitHubLauncherLocation = {
  runId: string; target: AdapterExecutionTarget | null | undefined;
};

function githubOperationLauncherDirectory(input: GitHubLauncherLocation): string {
  // Only controller-generated run IDs may name a removable directory.
  if (!/^[a-zA-Z0-9_-]+$/.test(input.runId)) throw new Error("Invalid GitHub launcher run ID");
  return input.target?.kind === "remote"
    ? path.posix.join(input.target.remoteCwd, ".paperclip-runtime", "github", input.runId)
    : path.join(os.tmpdir(), "paperclip-github-runtime", input.runId);
}

/** Call only after execution settles, before releasing its remote environment lease. */
export async function cleanupGitHubOperationLaunchers(input: GitHubLauncherLocation): Promise<void> {
  const directory = githubOperationLauncherDirectory(input);
  if (input.target?.kind === "remote") {
    const result = await adapterExecutionTargetCommandRunner(input.target).execute({
      command: "sh", args: ["-c", `rm -rf -- ${shellQuote(directory)}`],
      cwd: input.target.remoteCwd, timeoutMs: 5_000,
    });
    if (result.exitCode !== 0) throw new Error("Could not clean managed GitHub launchers");
  } else {
    await fs.rm(directory, { recursive: true, force: true });
  }
}

View on GitHub (pinned to 01ad858492)

Solutions

  1. Pass only the controller-generated run ID matching [a-zA-Z0-9_-]+
  2. Sanitize/normalize the identifier upstream, e.g. runId.replace(/[^a-zA-Z0-9_-]/g, '')
  3. Verify the value is not a URL or path fragment — extract the bare ID first

Example fix

// before
cleanup({ runId: githubRun.url.split('/').pop() + '/artifacts' })
// after
const runId = githubRun.id.toString();
if (!/^[a-zA-Z0-9_-]+$/.test(runId)) throw new Error('bad run id');
cleanup({ runId, target });
Defensive patterns

Strategy: type-guard

Validate before calling

if (!/^[a-zA-Z0-9_-]+$/.test(runId)) {
  throw new Error(`runId must match [a-zA-Z0-9_-]+, got: ${runId}`);
}

Type guard

function isSafeRunId(v: unknown): v is string {
  return typeof v === 'string' && /^[a-zA-Z0-9_-]+$/.test(v);
}

Try / catch

try {
  cleanupLauncher({ runId, target });
} catch (e) {
  if (String(e.message) === 'Invalid GitHub launcher run ID') {
    console.error('sanitize the run ID before calling cleanup');
  } else throw e;
}

Prevention

When it happens

Trigger: Calling the launcher directory/cleanup API with a runId containing '/', '..', whitespace, unicode, or any character outside [a-zA-Z0-9_-]; passing a raw external identifier (e.g. a full GitHub run URL or numeric id with prefix) as runId.

Common situations: Downstream code concatenating identifiers like 'runs/12345' or 'run.id' instead of the sanitized controller run ID; test code passing placeholder IDs with slashes.

Understand the failure class

Background: "invalid id" errors: invalid identifier format — why libraries reject IDs before lookup, and how to fix them — this error's family across 37 libraries.

Related errors


AI-assisted analysis of paperclipai/paperclip@01ad858492 (2026-09-10). Data as JSON: /api/errors/16244b1bf8aa7dde. Report an issue: GitHub.