{"record":{"id":"36927bfa1cdea290","repo":"vxcontrol/pentagi","slug":"failed-to-patch-subtasks-for-task-d-w","errorCode":null,"errorMessage":"failed to patch subtasks for task %d: %w","messagePattern":"failed to patch subtasks for task (.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"backend/pkg/tools/flow_manager.go","lineNumber":928,"sourceCode":"\t\t\t\t\"Use %s to create a new task instead\",\n\t\t\taction.TaskID, SubmitFlowInputToolName))\n\t}\n\n\tpatch := SubtaskPatch{\n\t\tOperations: action.Operations,\n\t\tMessage:    action.Message,\n\t}\n\n\tif err := patch.Validate(); err != nil {\n\t\treturn \"\", fmt.Errorf(\"invalid subtask patch: %w\", err)\n\t}\n\n\tif len(action.Operations) == 0 {\n\t\treturn fmt.Sprintf(\"No operations provided — the subtask plan for task %d is unchanged.\", action.TaskID), nil\n\t}\n\n\tif err := t.handler(ctx, action.TaskID, patch); err != nil {\n\t\treturn \"\", fmt.Errorf(\"failed to patch subtasks for task %d: %w\", action.TaskID, err)\n\t}\n\n\t// Query the new subtask list so the LLM can correlate the patched entries with their new IDs.\n\tnewPlanned, err := t.db.GetTaskPlannedSubtasks(ctx, action.TaskID)\n\tif err != nil {\n\t\t// Not fatal — operations were applied; just warn and skip the list.\n\t\treturn fmt.Sprintf(\n\t\t\t\"%d operation(s) applied to the subtask plan for task %d. \"+\n\t\t\t\t\"Could not retrieve updated subtask list: %s. \"+\n\t\t\t\t\"Call %s with detail='planned' and task_id=%d to verify.\",\n\t\t\tlen(action.Operations), action.TaskID, err,\n\t\t\tGetFlowStatusToolName, action.TaskID), nil\n\t}\n\n\tsb := &strings.Builder{}\n\tfmt.Fprintf(sb,\n\t\t\"%d operation(s) applied to the subtask plan for task %d. \"+\n\t\t\t\"Updated planned subtasks (new IDs assigned after recreation):\\n\",","sourceCodeStart":910,"sourceCodeEnd":946,"githubUrl":"https://github.com/vxcontrol/pentagi/blob/ea665308baaff015b226f308438a68d929d0f29b/backend/pkg/tools/flow_manager.go#L910-L946","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Re-fetch the current plan with get_flow_status (detail=planned or subtasks) and retry the patch using fresh task/subtask ids.","Verify task_id was copied from get_flow_status detail=tasks output, not invented or taken from a different flow.","Split large patches into smaller ones so a remove followed by a modify of the same id is not attempted in one atomic batch.","If the wrapped cause is a DB/connection error, retry after the database recovers; check backend logs and PostgreSQL health."],"exampleFix":"// before: patching with a stale id\n{\"task_id\": 3, \"operations\": [{\"op\": \"modify\", \"id\": 17, \"title\": \"Updated\"}]}\n// after: re-read plan, then patch with a live id\n// GET get_flow_status detail=subtasks -> id 23\n{\"task_id\": 3, \"operations\": [{\"op\": \"modify\", \"id\": 23, \"title\": \"Updated\"}]}","handlingStrategy":"retry","validationCode":"// Before patching, confirm ids are current:\nstatus, _ := getFlowStatus(ctx, detail=\"subtasks\", taskID=taskID)\nliveIDs := map[int64]bool{}\nfor _, s := range status.Subtasks { liveIDs[s.ID] = true }\nfor _, op := range operations {\n    if op.ID != nil && !liveIDs[*op.ID] {\n        return fmt.Errorf(\"subtask id %d no longer exists; re-read plan\", *op.ID)\n    }\n}","typeGuard":null,"tryCatchPattern":"if _, err := toolHandle(ctx, action); err != nil {\n    if strings.HasPrefix(err.Error(), fmt.Sprintf(\"failed to patch subtasks for task %d:\", taskID)) {\n        // refresh plan via get_flow_status, rebuild operations with live ids, retry once\n        // if the cause is a DB error, back off and retry after the database recovers\n    }\n}","preventionTips":["Always fetch task_id and subtask ids from get_flow_status immediately before patching.","Apply remove-then-modify sequences as separate patch calls, not one batch referencing a just-removed id.","Monitor PostgreSQL health; transient connection errors surface here wrapped.","Keep patches small and idempotent so a failed batch can be safely retried."],"tags":["database","go","persistence","tool-arguments"],"backgroundTag":"stale-entity-reference","analyzedSha":"ea665308baaff015b226f308438a68d929d0f29b","analyzedAt":"2026-09-01T14:16:31.421Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}