vxcontrol/pentagi · warning
operation %d: subtask with id %d not found for removal
Error message
operation %d: subtask with id %d not found for removal
What it means
applySubtaskOperations checks each remove operation's ID against idToIdx, the index of currently known subtasks. If the referenced subtask ID does not exist in the flow's subtask list, the patch is rejected. This catches stale or hallucinated IDs from the LLM before any mutation happens.
Source
Thrown at backend/pkg/providers/subtask_patch.go:64
// First pass: process removals and modifications in-place
for i, op := range patch.Operations {
opLogger := logger.WithFields(logrus.Fields{
"operation_index": i,
"operation": op.Op,
"id": op.ID,
"after_id": op.AfterID,
})
switch op.Op {
case tools.SubtaskOpRemove:
if op.ID == nil {
err := fmt.Errorf("operation %d: remove operation missing required id field", i)
opLogger.Error(err.Error())
return nil, err
}
if _, ok := idToIdx[*op.ID]; !ok {
err := fmt.Errorf("operation %d: subtask with id %d not found for removal", i, *op.ID)
opLogger.Error(err.Error())
return nil, err
}
removed[*op.ID] = true
opLogger.WithField("subtask_id", *op.ID).Debug("marked subtask for removal")
case tools.SubtaskOpModify:
if op.ID == nil {
err := fmt.Errorf("operation %d: modify operation missing required id field", i)
opLogger.Error(err.Error())
return nil, err
}
if op.Title == "" && op.Description == "" {
err := fmt.Errorf("operation %d: modify operation missing both title and description fields", i)
opLogger.Error(err.Error())
return nil, err
}
idx, ok := idToIdx[*op.ID]View on GitHub (pinned to ea665308ba)
Solutions
- Retry the refiner call with the current, accurate subtask list in the prompt.
- Verify the prompt includes the up-to-date subtask IDs the model may reference.
- Tolerate-and-skip unknown removals upstream if partial application is acceptable for your use case.
- Use a stronger model that grounds IDs in the provided list.
Example fix
// before
{"op":"remove","id":99} // no subtask 99
// after: pick an existing id from the current list
{"op":"remove","id":2} Defensive patterns
Strategy: validation
Validate before calling
for i, op := range ops {
if op.Op == tools.SubtaskOpRemove {
if _, ok := idToIdx[*op.ID]; !ok {
return fmt.Errorf("op %d: id %d not in current subtasks", i, *op.ID)
}
}
} Type guard
func subtaskExists(id int64, idToIdx map[int64]int) bool {
_, ok := idToIdx[id]
return ok
} Try / catch
patched, err := applySubtaskOperations(ctx, logger, ops, subtasks)
if err != nil && strings.Contains(err.Error(), "not found for removal") {
// regenerate patch with the current subtask list
return regeneratePatch(ctx, subtasks)
} Prevention
- Always include the current subtask ID list in the refiner prompt.
- Regenerate the patch from fresh state if subtasks changed concurrently.
- Optionally skip unknown removals instead of failing the whole patch.
When it happens
Trigger: LLM emits {"op":"remove","id":N} where N is not among the current subtask IDs — fabricated ID, ID from a previous state, or duplicate removal after the list changed mid-patch.
Common situations: Model referencing subtask IDs from an outdated snapshot; hallucinated sequential IDs; concurrent modification of the subtask list between generation and application.
Related errors
- operation %d: remove operation missing required id field
- operation %d: modify operation missing required id field
- operation %d: modify operation missing both title and descri
- no subtasks generated for task %d
- operation %d: add requires title
AI-assisted analysis of vxcontrol/pentagi@ea665308ba (2026-09-01).
Data as JSON: /api/errors/e3b0315aab89b1d4.
Report an issue: GitHub.