can1357/oh-my-pi · error · Error

Handoff generation produced no content

Error message

Handoff generation produced no content

What it means

generateDocument treats an empty generation as a hard failure when the handoff was user-initiated: if the LLM turn completes without producing content, it throws instead of silently returning. For autoTriggered handoffs it instead returns undefined so maintenance can fall back to the next compaction method — the throw exists to avoid a misleading 'cancelled' result on explicit requests.

Source

Thrown at packages/coding-agent/src/session/session-handoff.ts:206

			const handoffText = this.#host.deobfuscateFromProvider(rawHandoffText);

			throwIfHandoffAborted(handoffSignal);
			if (!handoffText || handoffText.trim().length === 0) {
				// Empty/whitespace-only generation is a real failure, not a user
				// cancellation. #7904 stopped masking provider errors as "Handoff
				// cancelled"; an empty document is the remaining path that produced the
				// same misleading, undebuggable message (#7993).
				logger.warn("Handoff generation produced no content", {
					sessionId: this.#host.sessionId(),
					autoTriggered: options?.autoTriggered ?? false,
				});
				// Auto-handoff is best-effort: returning undefined lets maintenance fall
				// back to the next compaction method. A user-initiated handoff must
				// surface the failure instead of a silent, misleading "cancelled".
				if (options?.autoTriggered) {
					return undefined;
				}
				throw new Error("Handoff generation produced no content");
			}

			let savedPath: string | undefined;
			if (options?.autoTriggered && this.#host.settings.get("compaction.handoffSaveToDisk")) {
				const artifactsDir = this.#host.sessionManager.getArtifactsDir();
				if (artifactsDir) {
					const handoffFilePath = path.join(artifactsDir, createHandoffFileName());
					try {
						await Bun.write(handoffFilePath, `${handoffText}\n`);
						savedPath = handoffFilePath;
					} catch (error) {
						logger.warn("Failed to save handoff document to disk", {
							path: handoffFilePath,
							error: error instanceof Error ? error.message : String(error),
						});
					}
				} else {
					logger.debug("Skipping handoff document save because session is not persisted");

View on GitHub (pinned to 9690622007)

Solutions

  1. Retry the handoff generation, possibly with a different model or higher output limit
  2. Check provider logs/response for blocked or empty completions and fix the request (prompt, maxTokens)
  3. Switch to a non-handoff compaction method (e.g. summarize) as the fallback
  4. Catch the error for user-initiated compaction and surface a retry option

Example fix

// before
await handoff.generateDocument(signal); // throws on empty output
// after
try {
  await handoff.generateDocument(signal);
} catch (err) {
  if (err.message === "Handoff generation produced no content") {
    await session.compact(); // fallback method
    return;
  }
  throw err;
}
Defensive patterns

Strategy: fallback

Try / catch

try {
  await handoff.generateDocument(signal);
} catch (err) {
  if (err instanceof Error && err.message === "Handoff generation produced no content") {
    await session.compact({ method: "summarize" }); // alternative compaction
    return;
  }
  throw err;
}

Prevention

When it happens

Trigger: generateDocument (options.autoTriggered falsy) where the handoff LLM response contains no usable content (empty stream, model returned nothing, response filtered).

Common situations: Provider returned an empty/blocked response; max-tokens set too low; upstream API error swallowed into an empty result; model produced only tool calls with no text.

Related errors


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