can1357/oh-my-pi · warning · ToolError
Message must not be empty.
Error message
Message must not be empty.
What it means
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.
Source
Thrown at packages/coding-agent/src/vibe/runtime.ts:942
}
this.#records.delete(key);
throw error;
}
}
/**
* Send a message to a worker. Mid-turn and streaming → steering; mid-turn
* otherwise → queued for the next turn; idle/parked → starts a new
* background turn immediately.
*/
async send(session: ToolSession, args: { session: string; message: string }): Promise<VibeSendOutcome> {
const scope = this.ownerScope(session);
const record = this.#record(scope, args.session);
if (record.state === "dead") {
throw new ToolError(`Vibe session "${record.id}" is dead. Spawn a new one with vibe_spawn.`);
}
const message = args.message.trim();
if (!message) throw new ToolError("Message must not be empty.");
const registered = this.#registeredAgent(record);
if (AgentRegistry.global().get(record.id) && !registered) {
throw new ToolError(`Vibe session "${record.id}" no longer resolves to this parent session.`);
}
if (record.turn) {
const live = registered?.session;
if (live?.isStreaming) {
await live.steer(message);
record.lastActivityAt = Date.now();
return { id: record.id, mode: "steered" };
}
record.queue.push(message);
record.lastActivityAt = Date.now();
return { id: record.id, mode: "queued" };
}
if (!registered || (registered.status !== "idle" && registered.status !== "parked")) {View on GitHub (pinned to 9690622007)
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.
Example fix
// before
await registry.send(session, { session: id, message: maybeEmpty });
// after
const message = maybeEmpty.trim();
if (message) {
await registry.send(session, { session: id, message });
} Defensive patterns
Strategy: validation
Validate before calling
const trimmed = message.trim();
if (!trimmed) throw new Error('refusing to send an empty vibe message');
await registry.send(session, { session: id, message: trimmed }); Prevention
- 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.
When it happens
Trigger: Calling send(session, { session: '<id>', message: '' }) or message consisting only of whitespace (' ', '\n', '\t').
Common situations: 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.
Related errors
- Managed skill "${name}" needs a non-empty description.
- Managed skill "${name}" needs a non-empty body.
- Report cannot be empty.
- Search scope entries must be non-empty paths or globs
- Empty report. ${reportIssueDeviceUsage()}
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/d053023210bd5a08.
Report an issue: GitHub.