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

  1. Pass a non-empty message string with actual content.
  2. Guard the call site: skip the send entirely when the composed message trims to empty.
  3. 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

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


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/d053023210bd5a08. Report an issue: GitHub.