earendil-works/pi · error
No default stream function configured. Pass streamFn explici
Error message
No default stream function configured. Pass streamFn explicitly or call setDefaultStreamFn().
What it means
pi-agent-core ships no built-in provider runtime: Agent and the runAgentLoop/runAgentLoopContinue functions fall back to getDefaultStreamFn() when the caller omits streamFn, and that getter throws until the host registers an implementation with setDefaultStreamFn() (stream-fn.ts:17). The error means the process was wired without ever deciding how model calls reach a provider - it fails fast at construction/loop start, before any request is attempted.
Source
Thrown at packages/agent/src/stream-fn.ts:17
import type { StreamFn } from "./types.ts";
let defaultStreamFn: StreamFn | undefined;
/**
* Configure the fallback used by Agent and low-level loops when callers omit streamFn.
*
* Hosts that provide a default model runtime can install its stream function here
* without making pi-agent-core depend on a provider catalog or compatibility layer.
*/
export function setDefaultStreamFn(streamFn: StreamFn | undefined): void {
defaultStreamFn = streamFn;
}
export function getDefaultStreamFn(): StreamFn {
if (!defaultStreamFn) {
throw new Error("No default stream function configured. Pass streamFn explicitly or call setDefaultStreamFn().");
}
return defaultStreamFn;
}
View on GitHub (pinned to 4af9d21d3b)
Solutions
- Pass streamFn explicitly when constructing the Agent or calling runAgentLoop - immune to module-instance issues
- Otherwise call setDefaultStreamFn(stream) once at app startup, before any Agent is created
- If you already called it, check for duplicate copies of the agent package (npm ls, lockfile audit) and dedupe
- Make sure the module that performs setup is imported for its side effects early in the entrypoint
Example fix
// before
const agent = new Agent({ model }); // throws: no default stream function
// after - pass it explicitly (or register once at startup)
import { stream } from "@earendil-works/pi-ai";
const agent = new Agent({ model, streamFn: stream });
// or, at app startup:
setDefaultStreamFn(stream); Defensive patterns
Strategy: validation
Validate before calling
import { getDefaultStreamFn } from "@earendil-works/pi-agent-core";
function hasStreamRuntime(): boolean {
try {
getDefaultStreamFn();
return true;
} catch {
return false;
}
}
if (!hasStreamRuntime() && !options.streamFn) {
throw new Error("Configure a stream function before creating Agents");
} Try / catch
try {
const agent = new Agent({ model });
} catch (e) {
if (e instanceof Error && e.message.includes("No default stream function")) {
failFastWithConfigGuidance(); // wire streamFn or setDefaultStreamFn, then restart
}
throw e;
} Prevention
- Prefer passing streamFn explicitly at every Agent construction site
- Otherwise centralize setDefaultStreamFn(stream) in the app entrypoint before any Agent exists
- After dependency upgrades, run a smoke test that constructs an Agent to catch wiring regressions
- Deduplicate package versions in monorepos so the registered default and the reader share one module instance
When it happens
Trigger: Constructing new Agent({ model }) or calling runAgentLoop without streamFn before any setDefaultStreamFn call; registering the default in a different copy of the module (duplicate package installs or mixed ESM/CJS resolution) so the Agent reads an unset instance; calling setDefaultStreamFn(undefined) during teardown and then creating a new Agent.
Common situations: New host apps embedding the agent core without wiring a runtime; upgrades that moved or renamed the setup call; monorepos loading two versions of the package; import-order or tree-shaking changes dropping the setup module's side effect.
Related errors
- Cannot continue: no messages in context
- Cannot continue from message role: assistant
- No API key for provider: ${provider}
AI-assisted analysis of earendil-works/pi@4af9d21d3b (2026-08-24).
Data as JSON: /api/errors/418cc02309e64b3f.
Report an issue: GitHub.