paperclipai/paperclip · error

Invalid normalized OpenCode session id

Error message

Invalid normalized OpenCode session id

What it means

Thrown when normalizing a provider session id into a filesystem-safe path segment under the runtime directory produces an empty string, '.', or '..' — i.e. the id is unusable as a directory/file name. This guards against escaping the runtime directory (path traversal) and against empty identifiers.

Source

Thrown at packages/paperclip-runner/src/drivers/opencode/opencode-server-driver.ts:2483

    "HTTP_PROXY",
    "HTTPS_PROXY",
    "NO_PROXY",
    "ALL_PROXY",
    "SSL_CERT_FILE",
    "SSL_CERT_DIR",
    "OPENROUTER_API_KEY",
  ];
}

function sessionRoot(
  runtimeDirectory: string,
  normalizedSessionId: string,
): string {
  const safe = normalizedSessionId
    .replace(/[^a-zA-Z0-9._-]/g, "_")
    .slice(0, 120);
  if (!safe || safe === "." || safe === "..")
    throw new Error("Invalid normalized OpenCode session id");
  return join(resolve(runtimeDirectory), safe);
}

function validateWorkspace(value: string): string {
  const cwd = resolve(value);
  if (!value.trim() || cwd === dirname(cwd))
    throw new Error("OpenCode working directory must not be a filesystem root");
  return cwd;
}

function validModel(value: string): boolean {
  const slash = value.indexOf("/");
  return slash > 0 && slash < value.length - 1;
}

function compareVersion(left: string, right: string): number {
  const a = left.split(".").map(Number);
  const b = right.split(".").map(Number);

View on GitHub (pinned to 01ad858492)

Solutions

  1. Ensure the provider session id is assigned (from the OpenCode server create-session response) before any code resolves paths from it.
  2. Log the raw id passed in — if it is empty/undefined, fix the code path that reads the id field (possible API contract drift).
  3. Treat this as a traversal defense: never bypass the sanitizer; validate the id upstream instead (require /^[a-zA-Z0-9._-]+$/).
  4. If a placeholder path is needed before the id exists, generate a local uuid rather than passing an empty id.

Example fix

// before
const dir = sessionPath(runtimeDir, providerSessionId ?? "");

// after
if (!providerSessionId || !/^[a-zA-Z0-9._-]+$/.test(providerSessionId)) {
  throw new Error("Provider session id not yet assigned");
}
const dir = sessionPath(runtimeDir, providerSessionId);
Defensive patterns

Strategy: validation

Validate before calling

if (!providerSessionId || !/^[a-zA-Z0-9._-]+$/.test(providerSessionId)) {
  throw new Error("Provider session id missing or unsafe — cannot resolve runtime path");
}

Type guard

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

Try / catch

try {
  const dir = sessionPath(runtimeDir, providerSessionId);
} catch (e) {
  if (e instanceof Error && e.message === "Invalid normalized OpenCode session id") {
    // id empty/unset — ensure create-session response was processed first
  }
}

Prevention

When it happens

Trigger: normalizedSessionId contains only characters replaced by '_' mapping to '.'/'..' after sanitization, or is empty/whitespace — typically an unset or empty provider session id passed before the server assigned a real id.

Common situations: Calling session-path helpers before the first server response populated #providerSessionId; an upstream API change returning a different id field so undefined/empty is coerced into the sanitizer; ids crafted to collide with '..' for traversal.

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/77505d62fbd62691. Report an issue: GitHub.