can1357/oh-my-pi · error
unexpected-stop: no API key for ${model.provider}/${model.id
Error message
unexpected-stop: no API key for ${model.provider}/${model.id} What it means
After resolving a tiny/smol model for online stop classification, classifyOnline fetches an API key via registry.getApiKey. If none exists for that model's provider it throws this error naming provider/model. Classification cannot proceed without credentials.
Source
Thrown at packages/coding-agent/src/session/unexpected-stop-classifier.ts:95
return undefined;
} catch (error) {
logger.debug("unexpected-stop: classification failed", {
error: error instanceof Error ? error.message : String(error),
backend,
});
return undefined;
}
}
async function classifyOnline(text: string, deps: ClassifyUnexpectedStopDeps): Promise<boolean | undefined> {
const resolved = resolveRoleSelection(["tiny", "smol"], deps.settings, deps.registry.getAvailable());
const model = resolved?.model;
if (!model) {
throw new Error("unexpected-stop: no tiny/smol model available for classification");
}
const apiKey = await deps.registry.getApiKey(model, deps.sessionId);
if (!apiKey) {
throw new Error(`unexpected-stop: no API key for ${model.provider}/${model.id}`);
}
const metadata = deps.metadataResolver?.(model.provider);
const maxTokens = ONLINE_REASONING_SAFE_MAX_TOKENS;
const response = await retryTransientCompletion(
() =>
completeSimple(
model,
{
systemPrompt: [CLASSIFIER_SYSTEM_PROMPT],
messages: [{ role: "user", content: text, timestamp: Date.now() }],
},
{
apiKey: deps.registry.resolver(model, deps.sessionId),
maxTokens,
disableReasoning: true,
metadata,
signal: deps.signal,View on GitHub (pinned to 9690622007)
Solutions
- Add an API key for the named provider (env var or provider auth the registry reads).
- Point the tiny/smol role at a model whose provider already has credentials.
- Handle in classifyUnexpectedStop and fall back to local classification.
- Re-authenticate with the provider if the stored key was revoked.
Example fix
// before // smol role = openai/gpt-4o-mini but OPENAI_API_KEY unset // after export OPENAI_API_KEY=sk-... # or set smol role to a provider you have a key for
Defensive patterns
Strategy: validation
Validate before calling
const model = resolveRoleSelection(["tiny", "smol"], settings, registry.getAvailable())?.model;
if (model && !(await registry.getApiKey(model, sessionId))) {
throw new Error(`Set a key for ${model.provider} before enabling online classification`);
}
Try / catch
try {
verdict = await classifyUnexpectedStop(text, deps);
} catch (err) {
if (err instanceof Error && err.message.startsWith("unexpected-stop: no API key for")) {
verdict = undefined; // degrade gracefully to local classification
} else throw err;
} Prevention
- Keep keys configured for every provider any role (tiny/smol) may resolve to.
- Point small-model roles at providers you already authenticate with.
- Use the same secret source in CI as locally, or pass keys explicitly.
- Handle undefined verdicts in callers so missing keys degrade, not crash.
When it happens
Trigger: A tiny/smol model resolves (e.g. openai/gpt-4o-mini) but no API key is stored for that provider: env var unset, auth file missing the provider, or session-scoped key lookup fails.
Common situations: User authenticated their main provider but the small-model role points at a different provider; keys present locally but not in CI; key revoked/expired and removed from the registry.
Understand the failure class
Background: "API key is required" / "API key not found" / "No API key was set": the missing-api-key error family across 16 libraries — this error's family across 16 libraries.
Related errors
- Azure OpenAI API key is required. Set AZURE_OPENAI_API_KEY e
- Cursor API key (access token) is required
- DeepSeek API key is empty after stripping Bearer prefix
- No API key for ${model.provider}/${model.id}
- No API key available for model ${model.provider}/${model.id}
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/7ee4d30d4f7016a8.
Report an issue: GitHub.