JuliusBrussee/caveman · error · Error
cave_mastra_max_steps_invalid
cave_mastra_max_steps_invalid
Error message
cave_mastra_max_steps_invalid
What it means
extractTools iterates the parsed list, keeps only items that are JSON objects, unwraps OpenAI-style 'function' nesting, and requires a non-empty string 'name'. If no entry yields a name — every item was a non-object, a scalar array like [1,2,3], an array of strings, or objects missing/blank 'name' — it returns 'no named tools found'. The document parsed and had the right top-level shape, but contained zero recognizable tools.
Source
Thrown at packages/agent/src/adapters.ts:302
error?: unknown;
finishReason?: unknown;
response?: { modelId?: unknown };
}>;
}
export interface MastraAdapterOptions {
/** Mastra's per-generate maximum number of sequential LLM calls. */
maxSteps?: number;
}
export function createMastraAdapter(
identity: HarnessAdapterIdentity,
agent: MastraAgentBinding,
options: MastraAdapterOptions = {},
): HarnessAdapter {
const maxSteps = options.maxSteps;
if (maxSteps !== undefined && (!Number.isSafeInteger(maxSteps) || maxSteps <= 0)) {
throw new Error("cave_mastra_max_steps_invalid");
}
assertSupportedUpstream(identity, MASTRA_VERSION, "mastra", "@mastra/core");
return createHarnessAdapter("mastra", identity, {
package: "@mastra/core/agent",
class: "Agent",
method: "generate",
usage: "FullOutput.totalUsage",
...(maxSteps === undefined ? {} : { preExecutionControls: { maxSteps } }),
}, async (request) => {
const startedAt = performance.now();
const response = await agent.generate(request.prompt, {
maxProcessorRetries: 0,
runId: request.runID,
...(maxSteps === undefined ? {} : { maxSteps }),
...(request.signal === undefined ? {} : { abortSignal: request.signal }),
});
if (response.error !== undefined || terminalFinishReason(response.finishReason) === false) {
throw new Error("cave_mastra_terminal_failure");View on GitHub (pinned to 27d5a3981a)
Solutions
- Inspect the input: jq 'map(has("name"))' on arrays, or jq 'keys' on objects — confirm a 'name' string exists per tool.
- Fix the producer/wrapper to emit the standard shapes: MCP {"tools":[{"name":...}]}, OpenAI [{"function":{"name":...}}], or {"functions":[{"name":...}]}.
- If tools legitimately use a different name key, normalize it before piping: jq 'map(.toolName as $n | .name = $n)'.
- Handle empty catalogs upstream — decide whether an empty tool list should skip the shrink entirely.
Example fix
// before: catalog with non-standard name key
{"tools":[{"toolName":"read_file","inputSchema":{...}}]}
// after: normalized before piping
// jq '.tools |= map(.name = (.name // .toolName))' catalog.json | caveman-shrink
{"tools":[{"name":"read_file","inputSchema":{...}}]} Defensive patterns
Strategy: type-guard
Validate before calling
func hasNamedTools(b []byte) bool {
var top any
if json.Unmarshal(b, &top) != nil {
return false
}
var list []any
switch t := top.(type) {
case []any:
list = t
case map[string]any:
if v, ok := t["tools"].([]any); ok {
list = v
} else if v, ok := t["functions"].([]any); ok {
list = v
} else {
list = []any{t}
}
}
for _, item := range list {
obj, ok := item.(map[string]any)
if !ok {
continue
}
if fn, ok := obj["function"].(map[string]any); ok {
obj = fn
}
if n, _ := obj["name"].(string); n != "" {
return true
}
}
return false
} Try / catch
entries, err := extractTools(input)
if err != nil {
if strings.Contains(err.Error(), "no named tools found") {
// catalog parsed but had zero tools: skip shrink or alert on the producer
}
return err
} Prevention
- Assert each catalog entry has a non-empty string 'name' (or nested 'function.name') before piping.
- Normalize non-standard name keys (toolName, id) with jq before shrink.
- Treat empty tool catalogs as an expected case: short-circuit pipelines instead of erroring.
When it happens
Trigger: Piping an empty array ([]), an array of strings (["tool_a","tool_b"]), objects whose name is missing/blank/non-string (e.g. {name: 123}), or an object with neither 'tools' nor 'functions' keys and no top-level 'name' of its own.
Common situations: Fetching from the wrong endpoint that returns an unrelated object; a catalog filtered server-side to zero tools; name fields under a different key ("toolName", "id") than the expected "name"; OpenAI catalogs where the function wrapper lost its nested object.
Related errors
- cave_harness_upstream_version_mismatch
- cave_eve_terminal_${result.status}
- cave_claude_header_invalid
- caveman agent: invalid .caveman/provider.json
- caveman build: invalid .caveman/provider.json
AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15).
Data as JSON: /api/errors/13ec1893e15c40c0.
Report an issue: GitHub.