vxcontrol/pentagi · error

operation %d: remove requires id

Error message

operation %d: remove requires id

What it means

SubtaskPatch.Validate requires a non-nil ID for SubtaskOpRemove; this error fires when a remove operation has no ID pointer set. Without an ID the service cannot know which subtask to delete.

Source

Thrown at backend/pkg/tools/args.go:330

	TaskID     int64              `json:"task_id" jsonschema:"required,type=integer" jsonschema_description:"ID of the task whose subtask plan to modify. Obtain this from get_flow_status with detail='tasks'."`
	Operations []SubtaskOperation `json:"operations" jsonschema:"required" jsonschema_description:"Delta operations to apply: add (insert new subtask at a position), remove (delete by ID), modify (update title/description), reorder (move to different position). Empty array returns the current plan unchanged. Each operation's title/description, when present, is an engagement-log plan entry (see operations)."`
	Message    string             `json:"message" jsonschema:"required,title=Patch summary" jsonschema_description:"Engagement-log entry — a 1-2 short sentence running commentary describing what changes are being made to the plan. Written in the engagement language declared by your system prompt."`
}

// ValidateSubtaskPatch validates the operations in a SubtaskPatch
func (sp SubtaskPatch) Validate() error {
	for i, op := range sp.Operations {
		switch op.Op {
		case SubtaskOpAdd:
			if op.Title == "" {
				return fmt.Errorf("operation %d: add requires title", i)
			}
			if op.Description == "" {
				return fmt.Errorf("operation %d: add requires description", i)
			}
		case SubtaskOpRemove:
			if op.ID == nil {
				return fmt.Errorf("operation %d: remove requires id", i)
			}
		case SubtaskOpModify:
			if op.ID == nil {
				return fmt.Errorf("operation %d: modify requires id", i)
			}
			if op.Title == "" && op.Description == "" {
				return fmt.Errorf("operation %d: modify requires at least title or description", i)
			}
		case SubtaskOpReorder:
			if op.ID == nil {
				return fmt.Errorf("operation %d: reorder requires id", i)
			}
		default:
			return fmt.Errorf("operation %d: unknown operation type %q", i, op.Op)
		}
	}
	return nil
}

View on GitHub (pinned to ea665308ba)

Solutions

  1. Fetch the current plan (get_flow_status detail=subtasks) and copy the exact subtask ID into the remove operation.
  2. Guard in code: skip remove ops whose ID is nil, or resolve IDs before validation.
  3. Improve the tool prompt so the agent always includes the numeric subtask id for remove.

Example fix

// before
ops := []SubtaskOperation{{Op: SubtaskOpRemove}}
// after
ops := []SubtaskOperation{{Op: SubtaskOpRemove, ID: int64Ptr(42)}}
Defensive patterns

Strategy: type-guard

Validate before calling

for i, op := range ops {
    if op.Op == "remove" && op.ID == nil {
        return fmt.Errorf("op %d: remove needs id", i)
    }
}

Type guard

func removableOp(op SubtaskOperation) bool {
    return op.Op == SubtaskOpRemove && op.ID != nil
}

Try / catch

if err := patch.Validate(); err != nil {
    if strings.Contains(err.Error(), "remove requires id") {
        // refetch plan, map titles to IDs, rebuild the patch
    }
}

Prevention

When it happens

Trigger: Patch operation {"op":"remove"} without "id", produced by patch_flow_subtasks tool calls or programmatic SubtaskPatch construction.

Common situations: LLM attempting to 'remove the last subtask' without looking up its ID first; JSON where id was null; scripts copying add-shaped ops into remove ops.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


AI-assisted analysis of vxcontrol/pentagi@ea665308ba (2026-09-01). Data as JSON: /api/errors/0f4252ced3ddbd58. Report an issue: GitHub.