{"record":{"id":"d3a27009f410df1e","repo":"paperclipai/paperclip","slug":"model-cannot-be-empty-or-have-surrounding-whites","errorCode":null,"errorMessage":"--model cannot be empty or have surrounding whitespace.","messagePattern":"--model cannot be empty or have surrounding whitespace\\.","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"critical","filePath":"cli/src/commands/test-drive.ts","lineNumber":244,"sourceCode":"): ResolvedTestDriveBootstrap {\n  if (options.apiKey !== undefined && options.apiKeyEnv !== undefined) {\n    throw new Error(\"--api-key and --api-key-env are mutually exclusive.\");\n  }\n\n  const harness = options.harness ?? \"claude\";\n  const definition = HARNESS_DEFINITIONS[harness];\n  if (!definition) {\n    throw new Error(`Unsupported test-drive harness: ${String(harness)}.`);\n  }\n\n  const companyName = (options.companyName ?? \"Test Company\").trim();\n  const agentName = (options.agentName ?? \"CEO\").trim();\n  if (!companyName) throw new Error(\"--company-name cannot be empty.\");\n  if (!agentName) throw new Error(\"--agent-name cannot be empty.\");\n\n  const model = options.model;\n  if (model !== undefined && (!model || model.trim() !== model)) {\n    throw new Error(\"--model cannot be empty or have surrounding whitespace.\");\n  }\n  if (\n    harness === \"opencode\" &&\n    (!model || !/^openrouter\\/[^/\\s]+(?:\\/[^/\\s]+)*$/.test(model))\n  ) {\n    throw new Error(\n      \"OpenCode test drives require --model openrouter/<model>, with no empty path segments.\",\n    );\n  }\n\n  const sourceEnvName = options.apiKeyEnv?.trim() || definition.credentialTarget;\n  if (options.apiKeyEnv !== undefined && !/^[A-Za-z_][A-Za-z0-9_]*$/.test(sourceEnvName)) {\n    throw new Error(\"--api-key-env must name a valid environment variable.\");\n  }\n  const credential = options.apiKey ?? env[sourceEnvName];\n  if (!credential || credential.trim().length === 0) {\n    throw new Error(\n      `No credential found. Set ${sourceEnvName}, pass --api-key-env <variable>, or pass --api-key <value>.`,","sourceCodeStart":226,"sourceCodeEnd":262,"githubUrl":"https://github.com/paperclipai/paperclip/blob/01ad8584922b5d85292b1723cae71fa0d9b07a19/cli/src/commands/test-drive.ts#L226-L262","documentation":"While starting a provider turn, the transport polls for the provider turn-start signal, pumping events and checking failures each 10ms iteration until the deadline. If the runnerd process exits before the provider turn is observed as started, it throws this error — the turn cannot proceed on a dead runner.","triggerScenarios":"During `#startTurn`'s wait loop, `#runnerHasExited()` returns true before `providerTurnStarted()` becomes true (runnerd crashed/exited early, e.g. bad spawn args, immediate panic, port conflict).","commonSituations":"Runnerd binary missing or crashing on startup; invalid model/config causing immediate exit; resource exhaustion (OOM) at turn start; environment differences after an upgrade where runnerd no longer boots.","solutions":["Check runnerd stdout/stderr and exit code to find why it exited at startup, and fix the spawn configuration or binary.","Verify the runnerd binary path/version is installed and executable in the environment.","Add health/startup validation before issuing a turn so a broken runner is detected earlier, then retry once healthy."],"exampleFix":"// before\nconst transport = new RunnerdCodexTransport({ runnerdPath: \"/opt/runnerd\" }); // missing binary\n// after\nif (!existsSync(runnerdPath)) await installRunnerd(runnerdPath);\nconst transport = new RunnerdCodexTransport({ runnerdPath });","handlingStrategy":"try-catch","validationCode":"if (!existsSync(runnerdPath) || !(await isExecutable(runnerdPath))) {\n  throw new Error(`runnerd binary not ready at ${runnerdPath}`);\n}","typeGuard":"null","tryCatchPattern":"try {\n  await transport.startTurn(input);\n} catch (e) {\n  if (e.message === \"runnerd exited before provider turn startup\") {\n    const { code, stderr } = await transport.getLastExitInfo();\n    log.error(\"runnerd exited\", { code, stderr });\n    await restartRunnerd(root);\n    await transport.startTurn(input); // one retry after restart\n  }\n}","preventionTips":["Capture and persist runnerd stderr/exit code for every startup failure.","Validate the runnerd binary path/version in a preflight health check.","Watch for repeated early-exit loops and back off instead of hammering restarts."],"tags":["runner","process-exit","turn-startup"],"backgroundTag":"connection-refused","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"}