vxcontrol/pentagi · error

failed to patch subtasks for task %d: %w

Error message

failed to patch subtasks for task %d: %w

What it means

After the patch passes local validation, the handler calls t.handler (the flow manager's DB-backed patch function) to apply the operations to the task's planned subtasks. If persistence fails — unknown task ID, referenced subtask ID does not exist, or a database error — the error is wrapped as "failed to patch subtasks for task %d: %w". No partial application is reported as success; either the whole patch applies or this error is returned.

Source

Thrown at backend/pkg/tools/flow_manager.go:928

				"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.",
			len(action.Operations), action.TaskID, err,
			GetFlowStatusToolName, action.TaskID), nil
	}

	sb := &strings.Builder{}
	fmt.Fprintf(sb,
		"%d operation(s) applied to the subtask plan for task %d. "+
			"Updated planned subtasks (new IDs assigned after recreation):\n",

View on GitHub (pinned to ea665308ba)

Solutions

  1. Re-fetch the current plan with get_flow_status (detail=planned or subtasks) and retry the patch using fresh task/subtask ids.
  2. Verify task_id was copied from get_flow_status detail=tasks output, not invented or taken from a different flow.
  3. Split large patches into smaller ones so a remove followed by a modify of the same id is not attempted in one atomic batch.
  4. If the wrapped cause is a DB/connection error, retry after the database recovers; check backend logs and PostgreSQL health.

Example fix

// before: patching with a stale id
{"task_id": 3, "operations": [{"op": "modify", "id": 17, "title": "Updated"}]}
// after: re-read plan, then patch with a live id
// GET get_flow_status detail=subtasks -> id 23
{"task_id": 3, "operations": [{"op": "modify", "id": 23, "title": "Updated"}]}
Defensive patterns

Strategy: retry

Validate before calling

// Before patching, confirm ids are current:
status, _ := getFlowStatus(ctx, detail="subtasks", taskID=taskID)
liveIDs := map[int64]bool{}
for _, s := range status.Subtasks { liveIDs[s.ID] = true }
for _, op := range operations {
    if op.ID != nil && !liveIDs[*op.ID] {
        return fmt.Errorf("subtask id %d no longer exists; re-read plan", *op.ID)
    }
}

Try / catch

if _, err := toolHandle(ctx, action); err != nil {
    if strings.HasPrefix(err.Error(), fmt.Sprintf("failed to patch subtasks for task %d:", taskID)) {
        // refresh plan via get_flow_status, rebuild operations with live ids, retry once
        // if the cause is a DB error, back off and retry after the database recovers
    }
}

Prevention

When it happens

Trigger: Calling patch_flow_subtasks with a task_id that does not exist (or belongs to another flow); an operation referencing a subtask id that was already removed by an earlier operation in the same patch or by a previous call; the underlying SQL/DB connection failing while updating subtask rows.

Common situations: Stale ids: the agent patches using ids read several turns ago while another patch already removed/reordered them; hallucinated task_id not obtained from get_flow_status; transient Postgres outage or connection pool exhaustion during a long engagement.

Related errors


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