can1357/oh-my-pi · error · AIError.ProviderResponseError

An unknown error occurred

Error message

An unknown error occurred

What it means

consumeGoogleStream completed with output.stopReason "aborted" or "error" and output.errorMessage was not populated, so this generic fallback text is thrown as AIError.ProviderResponseError (kind "output"). The Google stream signalled a failed terminal state without a reason string.

Source

Thrown at packages/ai/src/providers/google-shared.ts:774

			calculateCost(model, output.usage);
		}
	}

	flushCurrent();

	if (options?.signal?.aborted) {
		throw new AIError.AbortError();
	}

	if (!sawFinishReason) {
		throw new AIError.ProviderResponseError(
			"Google API stream ended without a finish reason (connection dropped or response truncated)",
			{ provider: model.provider, kind: "incomplete-stream" },
		);
	}

	if (output.stopReason === "aborted" || output.stopReason === "error") {
		throw new AIError.ProviderResponseError(output.errorMessage ?? "An unknown error occurred", {
			provider: model.provider,
			kind: "output",
		});
	}
}

/**
 * Generation/sampling fields that map directly onto Gemini's `GenerateContentConfig`.
 * Excludes any provider-specific extensions (`topP`/`topK`/etc are all forwarded as-is).
 */
interface GoogleGenerationConfig extends GenerateContentConfig {
	topP?: number;
	topK?: number;
	minP?: number;
	presencePenalty?: number;
	repetitionPenalty?: number;
}

View on GitHub (pinned to 9690622007)

Solutions

  1. If stopReason is "aborted", verify options.signal — this may be an intentional user abort, handle it as AbortError instead of a provider failure
  2. Capture partial output/accumulated chunks before the throw to diagnose what the model produced
  3. Retry on "error" stop reasons like MALFORMED_FUNCTION_CALL; reduce or fix tool schemas to avoid malformed calls
  4. Check Google service status for backend incidents

Example fix

// before
if (output.stopReason === "aborted" || output.stopReason === "error") {
  throw new AIError.ProviderResponseError(output.errorMessage ?? "An unknown error occurred", { provider: model.provider, kind: "output" });
}
// after
if (output.stopReason === "aborted" || output.stopReason === "error") {
  throw new AIError.ProviderResponseError(
    output.errorMessage ?? `Google stream ended with stopReason=${output.stopReason}`,
    { provider: model.provider, kind: "output" },
  );
}
Defensive patterns

Strategy: try-catch

Validate before calling

null

Type guard

function isAbortOrErrorStop(o: { stopReason: string }): boolean {
  return o.stopReason === "aborted" || o.stopReason === "error";
}

Try / catch

try {
  await streamGoogle(model, params, { signal });
} catch (err) {
  if (err instanceof AIError.AbortError || signal.aborted) throw err; // user-intent abort
  if (err instanceof AIError.ProviderResponseError && err.context?.kind === "output" && err.message === "An unknown error occurred") {
    logger.warn("google stream failed without message; retrying", {});
    return withBackoff(() => streamGoogle(model, params, { signal }));
  }
  throw err;
}

Prevention

When it happens

Trigger: Final chunk carries finishReason mapping to "error" (e.g. MALFORMED_FUNCTION_CALL, LANGUAGE, UNEXPECTED_TOOL_CALL, NO_IMAGE) or "aborted" (user abort via signal), and errorMessage was never set from prior error chunks.

Common situations: Model emits a malformed function call that Google terminates with MALFORMED_FUNCTION_CALL; user aborts mid-generation (check signal first); Google internal error terminations with no message.

Related errors


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