Every error thrown or rejected by bare-agent is prefixed with [ComponentName]. Use this page to look up what went wrong and how to fix it.
bare-agent provides typed error classes for programmatic error handling. All extend Error.
Error
└── BareAgentError { code, retryable, context }
├── ProviderError { status, body } — auto retryable for 429/5xx
├── ToolError code: 'TOOL_ERROR', retryable: false
├── TimeoutError code: 'ETIMEDOUT', retryable: true
├── ValidationError code: 'VALIDATION_ERROR', retryable: false
├── CircuitOpenError code: 'CIRCUIT_OPEN', retryable: true
└── MaxRoundsError code: 'MAX_ROUNDS', retryable: false
| Class |
When thrown |
retryable |
ProviderError |
HTTP 4xx/5xx from any provider |
true for 429, 500-504; false otherwise |
ToolError |
Tool execute() throws during Loop |
false |
TimeoutError |
Retry per-attempt timeout exceeded |
true |
CircuitOpenError |
CircuitBreaker is open, request rejected |
true |
MaxRoundsError |
Loop exceeded maxRounds without final response |
false |
ValidationError |
Input validation failures |
false |
Import: const { ProviderError, CircuitOpenError } = require('bare-agent');
Usage: try { ... } catch (err) { if (err instanceof ProviderError && err.status === 429) { /* rate limited */ } }
The retryable property integrates with Retry's fast path — errors with retryable: true are automatically retried without checking status codes.
| Error |
When |
Fix |
Circuit "key" is open (CircuitOpenError) |
Too many failures (>= threshold) and resetAfter hasn't elapsed |
Wait for the circuit to transition to half-open, or call cb.reset(key) |
The circuit breaker tracks failures per key. After threshold failures, calls are rejected immediately with CircuitOpenError until resetAfter ms elapse. The first call after that enters half-open state — success closes the circuit, failure reopens it.
| Error |
When |
Fix |
[FallbackProvider] all providers failed (AggregateError) |
Every provider in the list threw |
Check err.errors array for individual provider failures. Ensure at least one provider is configured correctly |
[FallbackProvider] requires at least one provider |
Constructor called with empty array |
Pass at least one provider |
| Error |
When |
Fix |
[Loop] requires a provider |
new Loop() called without options.provider |
Pass a provider: new Loop({ provider }) |
[Loop] Tool is missing a name |
A tool in the tools array has no name or a non-string name |
Every tool must have name: 'string' |
[Loop] Tool "X" is missing an execute() function |
A tool's execute is missing or not a function |
Add execute: async (args) => result to the tool |
[Loop] Tool "X" has invalid parameters |
parameters is present but not an object |
Set parameters to a JSON Schema object or remove it |
[Loop] Unknown tool: X |
LLM requested a tool not in the tools array |
Not an error you throw — fed back to the LLM so it can self-correct |
[Loop] Tool error: ... |
A tool's execute() threw during the loop |
Not an error you throw — fed back to the LLM. Check the tool's implementation |
[Loop] ended after N rounds without final response |
LLM kept requesting tool calls past maxRounds |
Increase maxRounds or simplify the task. Throws MaxRoundsError by default (v0.3.0+). Pass throwOnError: false for v0.2.x behavior (returned in result.error) |
Note: [Loop] Tool "X" has a non-string description is a console.warn, not a thrown error.
| Error |
When |
Fix |
[Planner] requires a provider |
new Planner() called without options.provider |
Pass a provider: new Planner({ provider }) |
[Planner] could not parse plan from LLM output |
LLM response doesn't contain a JSON array |
Check the LLM model — some models struggle with structured output. Try a more capable model |
[Planner] expected JSON array |
LLM returned valid JSON but not an array |
Same as above — the planning prompt expects an array |
[Planner] step missing id or action |
A step in the parsed array lacks id or action |
LLM output is malformed. Try a different model or customize the prompt |
| Error |
When |
Fix |
[StateMachine] Invalid transition: STATUS + EVENT (task: ID) |
Attempted a state transition not allowed by the transition table |
Check the allowed transitions: pending→start, running→complete/fail/pause/cancel, failed→retry/cancel, waiting_for_input→resume/cancel. Terminal states (done, cancelled) have no outgoing transitions |
| Error |
When |
Fix |
[Scheduler] Cannot parse schedule: "X" |
Schedule string is not relative (5s/30m/2h/1d) and not valid cron |
Use relative format (30m, 2h) or a cron expression (0 7 * * 1-5). Cron requires cron-parser package |
Handler errors: If a handler passed to start() throws, the error is passed to onError(err, job) if configured. The tick loop continues — handler errors never crash the scheduler.
| Error |
When |
Fix |
[Checkpoint] send and waitForReply callbacks required |
ask() called but send or waitForReply was not provided in constructor |
Pass both callbacks: new Checkpoint({ tools: [...], send: fn, waitForReply: fn }) |
| Error |
When |
Fix |
[Memory] requires options.store |
new Memory() called without options.store |
Pass a store: new Memory({ store: new JsonFileStore({ path }) }) |
| Error |
When |
Fix |
[Retry] Timeout (TimeoutError) |
A single attempt exceeded the configured timeout (default 60s) |
Increase timeout in constructor or per-call options. instanceof TimeoutError, code: 'ETIMEDOUT', retryable: true |
Note: After exhausting maxAttempts, Retry rethrows the last error from the wrapped function — it does not add its own prefix. Errors with err.retryable === true are automatically retried; err.retryable === false bail immediately without consuming remaining attempts.
| Error |
When |
Fix |
[CLIPipeProvider] requires command |
Constructor called without command |
Pass a command: new CLIPipeProvider({ command: 'claude', args: ['--print'] }) |
[CLIPipeProvider] failed to spawn: ... |
CLI binary not found or not executable (ENOENT, EACCES) |
Check that the command is installed and in PATH |
[CLIPipeProvider] process exited with code N: ... |
CLI tool returned non-zero exit |
Check stderr output (included in error message). May be bad args or tool-specific error |
[CLIPipeProvider] timed out after Nms |
CLI tool didn't produce output within timeout |
Increase timeout in constructor. Default is 30000ms |
[CLIPipeProvider] process produced no output |
CLI tool exited 0 but wrote nothing to stdout |
Check the command actually produces output for the given input |
systemPromptFlag behavior: When systemPromptFlag is set (e.g. '--system'), system messages are extracted from the messages array, joined with \n\n, and passed as a CLI argument ([flag, content]) appended after this.args. Non-system messages are formatted normally via _formatPrompt() and piped to stdin. If no system messages are present, the flag is not added. This prevents _formatPrompt() from flattening structured system prompts into System: ... plaintext, which breaks tools like claude --print that expect system content via a dedicated flag.
| Error |
When |
Fix |
[runPlan] steps must be a non-empty array |
Called with empty array or non-array |
Pass the output of Planner.plan() |
[runPlan] executeFn must be a function |
Second argument is not a function |
Pass an async function: async (step) => result |
[runPlan] duplicate step id: "X" |
Two steps share the same id |
Ensure all step ids are unique |
[runPlan] step "X" depends on unknown step "Y" |
dependsOn references a non-existent id |
Check that all dependency ids exist in the steps array |
runPlan non-thrown errors (returned in results array, not thrown):
dependency 'sN' failed — a step's dependency failed, so this step was skipped. Fix the upstream step.
validate() never throws. All errors are captured in the return value:
result.provider.error — provider generate() failed (auth, network, etc.)
result.store.error — store write/read/delete cycle failed
result.tools.errors[] — same validation messages as run() but collected, not thrown
| Error |
When |
Fix |
[OpenAIProvider] HTTP NNN / [OpenAIProvider] <api message> |
OpenAI API returned a 4xx/5xx response |
Check API key, model name, rate limits. Error has .status and .body properties |
[OpenAIProvider] Invalid JSON response |
Response body is not valid JSON |
Likely a network issue or proxy interference. Includes first 200 chars of the response |
| Error |
When |
Fix |
[AnthropicProvider] requires apiKey |
new AnthropicProvider() called without apiKey |
Pass your API key: new AnthropicProvider({ apiKey }) |
[AnthropicProvider] HTTP NNN / [AnthropicProvider] <api message> |
Anthropic API returned a 4xx/5xx response |
Check API key, model name, rate limits. Error has .status and .body properties |
[AnthropicProvider] Invalid JSON response |
Response body is not valid JSON |
Likely a network issue. Includes first 200 chars of the response |
| Error |
When |
Fix |
[OllamaProvider] HTTP NNN / [OllamaProvider] <api message> |
Ollama returned a 4xx/5xx response |
Check that Ollama is running and the model is pulled |
[OllamaProvider] Invalid JSON response |
Response body is not valid JSON |
Check Ollama is responding correctly. Includes first 200 chars of the response |
| Error |
When |
Fix |
[SQLiteStore] requires options.path |
new SQLiteStore() called without path |
Pass a database path: new SQLiteStore({ path: './memory.db' }) |
[SQLiteStore] requires better-sqlite3 |
better-sqlite3 package is not installed |
Install the peer dep: npm install better-sqlite3 |
| Error |
When |
Fix |
[JsonFileStore] requires options.path |
new JsonFileStore() called without path |
Pass a file path: new JsonFileStore({ path: './memory.json' }) |