vxcontrol/pentagi · error
invalid subtask patch: %w
Error message
invalid subtask patch: %w
What it means
The patch_flow_subtasks tool handler builds a SubtaskPatch from the LLM-provided operations and runs SubtaskPatch.Validate() before applying anything. Validation enforces per-operation-type invariants: add requires title and description; remove/modify/reorder require an id; modify requires at least title or description; and op must be one of add/remove/modify/reorder. Any violation aborts the whole patch with "invalid subtask patch: %w".
Source
Thrown at backend/pkg/tools/flow_manager.go:920
"Call %s first, then retry",
action.TaskID, st.ID, st.Title, StopFlowToolName))
}
}
return "", stateGuard(fmt.Errorf(
"no 'created' subtasks found for task %d; "+
"all subtasks have been executed or the task has no plan yet. "+
"Use %s to create a new task instead",
action.TaskID, SubmitFlowInputToolName))
}
patch := SubtaskPatch{
Operations: action.Operations,
Message: action.Message,
}
if err := patch.Validate(); err != nil {
return "", fmt.Errorf("invalid subtask patch: %w", err)
}
if len(action.Operations) == 0 {
return fmt.Sprintf("No operations provided — the subtask plan for task %d is unchanged.", action.TaskID), nil
}
if err := t.handler(ctx, action.TaskID, patch); err != nil {
return "", fmt.Errorf("failed to patch subtasks for task %d: %w", action.TaskID, err)
}
// Query the new subtask list so the LLM can correlate the patched entries with their new IDs.
newPlanned, err := t.db.GetTaskPlannedSubtasks(ctx, action.TaskID)
if err != nil {
// Not fatal — operations were applied; just warn and skip the list.
return fmt.Sprintf(
"%d operation(s) applied to the subtask plan for task %d. "+
"Could not retrieve updated subtask list: %s. "+
"Call %s with detail='planned' and task_id=%d to verify.",View on GitHub (pinned to ea665308ba)
Solutions
- Read the wrapped cause (e.g. "operation 2: modify requires at least title or description") and fix that specific operation in the tool call arguments.
- Use only the enum values add, remove, modify, reorder for the op field.
- For add, supply both title and description; for remove/modify/reorder, always include the numeric subtask id copied from get_flow_status output.
- If no changes are needed, send an empty operations array (handled gracefully) instead of a malformed placeholder operation.
Example fix
// before
{"task_id": 3, "operations": [{"op": "update", "title": "Scan web app"}]}
// after
{"task_id": 3, "operations": [{"op": "add", "title": "Scan web app", "description": "Enumerate and scan the web application for vulnerabilities"}]} Defensive patterns
Strategy: validation
Validate before calling
validOps := map[string]bool{"add": true, "remove": true, "modify": true, "reorder": true}
for i, op := range operations {
if !validOps[string(op.Op)] {
return fmt.Errorf("operation %d: unknown op %q", i, op.Op)
}
if op.Op == "add" && (op.Title == "" || op.Description == "") {
return fmt.Errorf("operation %d: add requires title and description", i)
}
if (op.Op == "remove" || op.Op == "modify" || op.Op == "reorder") && op.ID == nil {
return fmt.Errorf("operation %d: %s requires id", i, op.Op)
}
if op.Op == "modify" && op.Title == "" && op.Description == "" {
return fmt.Errorf("operation %d: modify requires title or description", i)
}
} Try / catch
if _, err := toolHandle(ctx, action); err != nil {
var invalid *fmt.WrapError // or: strings.Contains(err.Error(), "invalid subtask patch")
if strings.HasPrefix(err.Error(), "invalid subtask patch:") {
// do NOT retry as-is; log err and regenerate a schema-conformant patch
}
} Prevention
- Copy op enum values verbatim from the tool schema: add, remove, modify, reorder.
- Always pair add with both title and description.
- Always include the numeric id for remove/modify/reorder, taken from a fresh get_flow_status read.
- Send an empty operations array when nothing needs to change instead of a dummy operation.
When it happens
Trigger: Calling patch_flow_subtasks where operations[i] has: an unknown op value (typo like "update" or "delete" instead of "modify"/"remove"); an add without title or without description; a remove/modify/reorder missing the id field; or a modify that sets neither title nor description. The failing operation index and reason are embedded in the wrapped message.
Common situations: An LLM emits "op": "delete" or "move" instead of the enum names; the model omits id because it assumed positional indexing; add operations produced with empty description because the model only wrote a title; hand-crafted JSON in tests that predates the Validate() rules.
Related errors
- failed to unmarshal search arguments: %w
- query parameter is required
- failed to switch provider: %w
- failed to set provider: %w
- failed to perform agent chain for subtask %d: %w
AI-assisted analysis of vxcontrol/pentagi@ea665308ba (2026-09-01).
Data as JSON: /api/errors/0de6a8ef5e8ed61f.
Report an issue: GitHub.