{"record":{"id":"4619678acf73d287","repo":"vxcontrol/pentagi","slug":"failed-to-set-subtask-d-result-w","errorCode":null,"errorMessage":"failed to set subtask %d result: %w","messagePattern":"failed to set subtask (.+?) result: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"backend/pkg/controller/subtask.go","lineNumber":254,"sourceCode":"\treturn nil\n}\n\nfunc (stw *subtaskWorker) GetResult(ctx context.Context) (string, error) {\n\tsubtask, err := stw.subtaskCtx.DB.GetSubtask(ctx, stw.subtaskCtx.SubtaskID)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\treturn subtask.Result, nil\n}\n\nfunc (stw *subtaskWorker) SetResult(ctx context.Context, result string) error {\n\t_, err := stw.subtaskCtx.DB.UpdateSubtaskResult(ctx, database.UpdateSubtaskResultParams{\n\t\tResult: result,\n\t\tID:     stw.subtaskCtx.SubtaskID,\n\t})\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to set subtask %d result: %w\", stw.subtaskCtx.SubtaskID, err)\n\t}\n\n\treturn nil\n}\n\nfunc (stw *subtaskWorker) PutInput(ctx context.Context, input string) error {\n\tif stw.IsCompleted() {\n\t\treturn fmt.Errorf(\"subtask has already completed\")\n\t}\n\n\tif !stw.IsWaiting() {\n\t\treturn fmt.Errorf(\"subtask is not waiting, run first\")\n\t}\n\n\terr := stw.subtaskCtx.Provider.PutInputToAgentChain(ctx, stw.subtaskCtx.MsgChainID, input)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to put input for subtask %d: %w\", stw.subtaskCtx.SubtaskID, err)\n\t}","sourceCodeStart":236,"sourceCodeEnd":272,"githubUrl":"https://github.com/vxcontrol/pentagi/blob/ea665308baaff015b226f308438a68d929d0f29b/backend/pkg/controller/subtask.go#L236-L272","documentation":"SetResult persists the subtask's final result text via UpdateSubtaskResult. When that SQL update fails, the error is wrapped as 'failed to set subtask %d result'. The subtask keeps its previous (usually empty) result, so downstream consumers reading the result get nothing.","triggerScenarios":"Calling SetResult(ctx, result) when the DB UPDATE fails: connection drop, cancelled ctx, the subtask row was deleted (no rows error is not special-cased here), or a malformed/large result text rejected by the column type.","commonSituations":"Flow or task deleted concurrently while the worker tries to write its result; database connection pool exhausted under many parallel subtasks; result string exceeding column limits.","solutions":["Inspect the wrapped pgx/pq error: for undefined-table or migration errors run the goose migrations.","If sql.ErrNoRows, the subtask was deleted (e.g. by flow replacement) — abandon the stale worker rather than retrying.","Retry the update with a fresh context (context.Background()+timeout) if the original ctx was cancelled before the write.","Truncate oversized result strings before calling SetResult to avoid column-limit rejections."],"exampleFix":"// before\nif err := stw.SetResult(ctx, bigResult); err != nil {\n\treturn err\n}\n// after\nresult := bigResult\nif len(result) > 1<<20 {\n\tresult = result[:1<<20]\n}\nif err := stw.SetResult(ctx, result); err != nil {\n\tif errors.Is(err, context.Canceled) {\n\t\tresetCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)\n\t\tdefer cancel()\n\t\treturn stw.SetResult(resetCtx, result)\n\t}\n\treturn err\n}","handlingStrategy":"retry","validationCode":"// ensure the subtask row still exists before writing the result\nvar exists bool\nerr := db.QueryRowContext(ctx, `SELECT EXISTS(SELECT 1 FROM subtasks WHERE id = $1)`, subtaskID).Scan(&exists)\nif err != nil || !exists {\n\t// stale worker: skip result write\n}","typeGuard":"func isRetryableDBErr(err error) bool {\n\treturn isContextErr(err) || errors.Is(err, pgx.ErrTxClosed)\n}","tryCatchPattern":"if err := worker.SetResult(ctx, result); err != nil {\n\tif isRetryableDBErr(err) {\n\t\tresetCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)\n\t\tdefer cancel()\n\t\treturn worker.SetResult(resetCtx, result)\n\t}\n\treturn err\n}","preventionTips":["Truncate oversized results before persisting.","Retry transient DB errors with exponential backoff.","Verify migrations are applied so the subtasks table/columns exist.","Treat sql.ErrNoRows as 'worker is stale' and abandon rather than retry."],"tags":["go","database","persistence"],"backgroundTag":"database-update-failed","analyzedSha":"ea665308baaff015b226f308438a68d929d0f29b","analyzedAt":"2026-09-01T14:16:31.421Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}