can1357/oh-my-pi · error · ToolError
`ops` must include at least one op entry
Error message
`ops` must include at least one op entry
What it means
ast_edit requires at least one rewrite op. After mapping (and possibly filtering) params.ops into the ops tuples, an empty array means the call would do nothing, so the tool throws ToolError instead of performing a no-op run.
Source
Thrown at packages/coding-agent/src/tools/ast-edit.ts:275
this.description = prompt.render(astEditDescription);
}
async execute(
_toolCallId: string,
params: AstEditSchemaInfer,
signal?: AbortSignal,
_onUpdate?: AgentToolUpdateCallback<AstEditToolDetails>,
_context?: AgentToolContext,
): Promise<AgentToolResult<AstEditToolDetails>> {
return untilAborted(signal, async () => {
const ops = params.ops.map((entry, index) => {
if (entry.pat.length === 0) {
throw new ToolError(`\`ops[${index}].pat\` must be a non-empty pattern`);
}
return [entry.pat, entry.out] as const;
});
if (ops.length === 0) {
throw new ToolError("`ops` must include at least one op entry");
}
const seenPatterns = new Set<string>();
for (const [pat] of ops) {
if (seenPatterns.has(pat)) {
throw new ToolError(`Duplicate rewrite pattern: ${pat}`);
}
seenPatterns.add(pat);
}
const normalizedRewrites = Object.fromEntries(ops);
const maxFiles = $envpos("PI_MAX_AST_FILES", 1000);
const scope = await resolveToolSearchScope({
rawPaths: params.paths,
cwd: this.session.cwd,
internalUrlAction: "rewrite",
settings: this.session.settings,
signal,
sessionFile: this.session.getSessionFile() ?? undefined,View on GitHub (pinned to 9690622007)
Solutions
- Include at least one { pat, out } entry in ops.
- Skip the ast_edit call entirely in the caller when no rewrites are needed instead of sending an empty list.
- Fix argument construction so ops is not dropped/emptied before dispatch.
Example fix
// before
if (ops.length >= 0) await astEdit.execute({ path, ops });
// after
if (ops.length > 0) await astEdit.execute({ path, ops }); Defensive patterns
Strategy: validation
Validate before calling
if (!Array.isArray(ops) || ops.length === 0) {
throw new Error('ast_edit requires at least one op');
} Type guard
function hasOps(p: { ops?: unknown }): p is { ops: unknown[] } {
return Array.isArray(p.ops) && p.ops.length > 0;
} Prevention
- Guard the call site: only invoke ast_edit when ops.length > 0.
- When ops are built conditionally, skip the tool call entirely instead of sending an empty array.
- Enforce minItems: 1 in the tool schema for callers that validate JSON schema.
When it happens
Trigger: Calling ast_edit with `ops: []`, or with an ops value that maps to an empty list (e.g. empty params.ops from defaulted/omitted arguments).
Common situations: Generated tool calls where the ops list was omitted or emptied by preprocessing; a caller that conditionally builds ops and invokes the tool even when no rewrites are needed.
Related errors
- rewrite received invalid arguments: ${params.summary}
- approve received invalid arguments: ${params.summary}
- `ops[${index}].pat` must be a non-empty pattern
- Duplicate rewrite pattern: ${pat}
- Cannot choose from an empty header profile list
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/c8f9c8e3617cd284.
Report an issue: GitHub.