{"record":{"id":"2ca3b037cd5404c7","repo":"paperclipai/paperclip","slug":"codex-working-directory-is-required","errorCode":null,"errorMessage":"Codex working directory is required","messagePattern":"Codex working directory is required","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/paperclip-runner/src/drivers/codex/codex-boundaries.ts","lineNumber":47,"sourceCode":"  \".ssh\",\n] as const;\n\nexport type CodexWorkingDirectoryAuthority =\n  \"local_filesystem\" | \"remote_runner\";\n\nfunction record(value: unknown): Record<string, unknown> {\n  return typeof value === \"object\" && value !== null && !Array.isArray(value)\n    ? (value as Record<string, unknown>)\n    : {};\n}\n\nexport function validateCodexWorkingDirectory(\n  workingDirectory: string,\n  environment: NodeJS.ProcessEnv = process.env,\n  authority: CodexWorkingDirectoryAuthority = \"local_filesystem\",\n): string {\n  if (workingDirectory.trim().length === 0) {\n    throw new Error(\"Codex working directory is required\");\n  }\n  if (authority === \"remote_runner\") {\n    return validateRemoteRunnerWorkingDirectory(workingDirectory, environment);\n  }\n  const requested = resolve(workingDirectory);\n  let resolved: string;\n  try {\n    resolved = realpathSync.native(requested);\n    if (!statSync(resolved).isDirectory()) {\n      throw new Error(\"Codex working directory must be a directory\");\n    }\n  } catch (error) {\n    const code = (error as NodeJS.ErrnoException).code;\n    if (code === \"ENOENT\") {\n      throw new Error(\n        \"Codex working directory must exist before provider admission\",\n      );\n    }","sourceCodeStart":29,"sourceCodeEnd":65,"githubUrl":"https://github.com/paperclipai/paperclip/blob/01ad8584922b5d85292b1723cae71fa0d9b07a19/packages/paperclip-runner/src/drivers/codex/codex-boundaries.ts#L29-L65","documentation":"`validateCodexWorkingDirectory` is the boundary gate every Codex run passes through before the provider is admitted. It rejects a working directory that is empty or whitespace-only, because the driver cannot spawn or sandbox a session without a concrete filesystem location. This is the first check in the validation chain.","triggerScenarios":"Calling `validateCodexWorkingDirectory(\"\")`, a string of spaces, or passing an undefined-but-coerced value from configuration where the workspace cwd was never assigned.","commonSituations":"Misconfigured task/run record with a null workspace path; environment provisioning failed to create the workspace field; a template that forgot to interpolate the working directory variable.","solutions":["Ensure the task/company record has a non-empty working directory before invoking the driver","Fix upstream config so the workspace path is populated (e.g. PAPERCLIP_WORKSPACE_CWD assignment logic)","Trim and validate user/config input before calling the driver"],"exampleFix":"// before\nconst cwd = task.workspaceCwd ?? \"\";\nvalidateCodexWorkingDirectory(cwd);\n// after\nif (!task.workspaceCwd) throw new Error(\"task has no assigned workspace\");\nvalidateCodexWorkingDirectory(task.workspaceCwd);","handlingStrategy":"validation","validationCode":"if (typeof cwd !== \"string\" || cwd.trim().length === 0) {\n  throw new Error(\"working directory must be a non-empty string\");\n}","typeGuard":"function isNonEmptyString(v: unknown): v is string {\n  return typeof v === \"string\" && v.trim().length > 0;\n}","tryCatchPattern":"try {\n  validateCodexWorkingDirectory(cwd);\n} catch (err) {\n  if (err.message === \"Codex working directory is required\") {\n    throw new ConfigError(`task ${task.id} has no assigned workspace path`);\n  }\n  throw err;\n}","preventionTips":["Validate task records for a populated workspace path at scheduling time","Fail fast on missing config instead of coercing undefined to \"\"","Add a schema check (zod/valibot) on the run configuration before driver creation"],"tags":["validation","configuration","codex"],"backgroundTag":"empty-required-field","analyzedSha":"01ad8584922b5d85292b1723cae71fa0d9b07a19","analyzedAt":"2026-09-10T03:14:50.855Z","contentChangedAt":"2026-09-10T03:14:50.855Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}