{"record":{"id":"d053023210bd5a08","repo":"can1357/oh-my-pi","slug":"message-must-not-be-empty","errorCode":null,"errorMessage":"Message must not be empty.","messagePattern":"Message must not be empty\\.","errorType":"validation","errorClass":"ToolError","httpStatus":null,"severity":"warning","filePath":"packages/coding-agent/src/vibe/runtime.ts","lineNumber":942,"sourceCode":"\t\t\t}\n\t\t\tthis.#records.delete(key);\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\t/**\n\t * Send a message to a worker. Mid-turn and streaming → steering; mid-turn\n\t * otherwise → queued for the next turn; idle/parked → starts a new\n\t * background turn immediately.\n\t */\n\tasync send(session: ToolSession, args: { session: string; message: string }): Promise<VibeSendOutcome> {\n\t\tconst scope = this.ownerScope(session);\n\t\tconst record = this.#record(scope, args.session);\n\t\tif (record.state === \"dead\") {\n\t\t\tthrow new ToolError(`Vibe session \"${record.id}\" is dead. Spawn a new one with vibe_spawn.`);\n\t\t}\n\t\tconst message = args.message.trim();\n\t\tif (!message) throw new ToolError(\"Message must not be empty.\");\n\t\tconst registered = this.#registeredAgent(record);\n\t\tif (AgentRegistry.global().get(record.id) && !registered) {\n\t\t\tthrow new ToolError(`Vibe session \"${record.id}\" no longer resolves to this parent session.`);\n\t\t}\n\n\t\tif (record.turn) {\n\t\t\tconst live = registered?.session;\n\t\t\tif (live?.isStreaming) {\n\t\t\t\tawait live.steer(message);\n\t\t\t\trecord.lastActivityAt = Date.now();\n\t\t\t\treturn { id: record.id, mode: \"steered\" };\n\t\t\t}\n\t\t\trecord.queue.push(message);\n\t\t\trecord.lastActivityAt = Date.now();\n\t\t\treturn { id: record.id, mode: \"queued\" };\n\t\t}\n\n\t\tif (!registered || (registered.status !== \"idle\" && registered.status !== \"parked\")) {","sourceCodeStart":924,"sourceCodeEnd":960,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/vibe/runtime.ts#L924-L960","documentation":"send() validates that the message contains non-whitespace content after trimming. An empty or whitespace-only message cannot be forwarded to the worker (it would neither steer, queue, nor start a meaningful turn), so the registry rejects it up front with this ToolError.","triggerScenarios":"Calling send(session, { session: '<id>', message: '' }) or message consisting only of whitespace (' ', '\\n', '\\t').","commonSituations":"Programmatic pipelines that build messages from variables that end up empty (failed template interpolation, empty file read); LLM-generated tool arguments with an omitted/blank message field; stripping content in preprocessing left nothing behind.","solutions":["Pass a non-empty message string with actual content.","Guard the call site: skip the send entirely when the composed message trims to empty.","If the message comes from a file or upstream output, check why it is empty before sending."],"exampleFix":"// before\nawait registry.send(session, { session: id, message: maybeEmpty });\n\n// after\nconst message = maybeEmpty.trim();\nif (message) {\n\tawait registry.send(session, { session: id, message });\n}","handlingStrategy":"validation","validationCode":"const trimmed = message.trim();\nif (!trimmed) throw new Error('refusing to send an empty vibe message');\nawait registry.send(session, { session: id, message: trimmed });","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always trim and check message content before calling send().","Validate tool arguments (message field) before invoking vibe_send.","Guard template-built messages: fail fast when interpolation yields empty text.","Never pass user-controlled strings without an emptiness check."],"tags":["vibe","validation","empty-input"],"backgroundTag":"empty-required-argument","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}