{"record":{"id":"b6af16f21f2715a4","repo":"can1357/oh-my-pi","slug":"params-op-requires-name","errorCode":null,"errorMessage":"${params.op} requires name","messagePattern":"(.+?) requires name","errorType":"validation","errorClass":"ToolError","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/tools/hub/launch.ts","lineNumber":173,"sourceCode":"/** Structured launch state retained for compact TUI rendering. */\nexport interface LaunchToolDetails {\n\top: LaunchParams[\"op\"];\n\tdaemon?: DaemonSnapshot;\n\tdaemons?: DaemonSnapshot[];\n\tcursor?: number;\n\ttimedOut?: boolean;\n\t/** logs: daemon lifecycle state at read time. */\n\tstate?: DaemonState;\n\t/** logs: virtual terminal rows for display; model-facing content remains sanitized text. */\n\tterminalRows?: string[];\n\t/** wait: output line that satisfied the pattern. */\n\tmatched?: string;\n\t/** describe: immutable launch spec backing the command/cwd detail lines. */\n\tspec?: DaemonSpec;\n}\n\nfunction requiredName(params: LaunchParams): string {\n\tif (!params.name) throw new ToolError(`${params.op} requires name`);\n\treturn params.name;\n}\n\nfunction timeoutMs(value: number | undefined, fallbackSeconds: number): number {\n\tconst seconds = Math.max(0.05, Math.min(3_600, value ?? fallbackSeconds));\n\treturn Math.round(seconds * 1_000);\n}\n\nfunction commandSpec(params: LaunchParams, session: ToolSession): DaemonSpec {\n\tconst name = requiredName(params);\n\tif (!params.application) throw new ToolError(\"start requires application\");\n\tconst ready = params.ready;\n\tconst detached = params.detached ?? false;\n\tif (ready?.port !== undefined && (!Number.isInteger(ready.port) || ready.port < 1 || ready.port > 65_535)) {\n\t\tthrow new ToolError(\"ready.port must be an integer from 1 to 65535\");\n\t}\n\tif (ready && !ready.log && ready.port === undefined) throw new ToolError(\"ready requires log or port\");\n\treturn {","sourceCodeStart":155,"sourceCodeEnd":191,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/tools/hub/launch.ts#L155-L191","documentation":"requiredName() validates LaunchParams for operations that address a named daemon (stop, send, log, etc.). If `params.name` is empty/undefined it throws a ToolError '<op> requires name', telling the caller which operation is missing the required daemon name.","triggerScenarios":"Calling the launch tool with an op such as 'stop', 'send', or 'log' but omitting the `name` parameter, or passing name: \"\".","commonSituations":"Model-generated tool args forgot the name field; code copies a start call and deletes name; template/automation builds params conditionally and the name branch was skipped.","solutions":["Add the `name` parameter identifying the daemon for the given op.","Verify the op actually needs a name — 'start' derives its name from params and does not go through this path.","Check spelling/casing of the parameter (must be `name`)."],"exampleFix":"// before\nlaunch({ op: \"stop\" })\n// throws: stop requires name\n\n// after\nlaunch({ op: \"stop\", name: \"dev-server\" })","handlingStrategy":"validation","validationCode":"if (!params.name?.trim()) throw new Error(`${params.op} requires name`);","typeGuard":null,"tryCatchPattern":"try {\n  await launchTool.run({ op: \"stop\", name });\n} catch (err) {\n  if (err instanceof ToolError && err.message.endsWith(\"requires name\")) {\n    // supply the daemon name and retry\n  } else throw err;\n}","preventionTips":["Always include name for ops other than start.","Validate tool args against the tool's JSON schema before invoking.","Trim and non-empty-check name values built from templates."],"tags":["validation","missing-parameter","tool-args"],"backgroundTag":"missing-required-parameter","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}