{"record":{"id":"5aec13eaa1a66712","repo":"vxcontrol/pentagi","slug":"failed-to-update-subtask-d-status-to-created-w","errorCode":null,"errorMessage":"failed to update subtask %d status to created: %w","messagePattern":"failed to update subtask (.+?) status to created: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"backend/pkg/controller/subtask.go","lineNumber":104,"sourceCode":"\tctx, span := obs.Observer.NewSpan(ctx, obs.SpanKindInternal, \"controller.LoadSubtaskWorker\")\n\tdefer span.End()\n\n\tvar completed, waiting bool\n\tswitch subtask.Status {\n\tcase database.SubtaskStatusFinished, database.SubtaskStatusFailed:\n\t\tcompleted = true\n\tcase database.SubtaskStatusWaiting:\n\t\twaiting = true\n\tcase database.SubtaskStatusRunning:\n\t\tvar err error\n\t\t// if subtask is running, it means that it was not finished by previous run\n\t\t// so we need to set it to created and continue from the beginning\n\t\tsubtask, err = taskCtx.DB.UpdateSubtaskStatus(ctx, database.UpdateSubtaskStatusParams{\n\t\t\tStatus: database.SubtaskStatusCreated,\n\t\t\tID:     subtask.ID,\n\t\t})\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"failed to update subtask %d status to created: %w\", subtask.ID, err)\n\t\t}\n\tcase database.SubtaskStatusCreated:\n\t\treturn nil, fmt.Errorf(\"subtask %d has created yet: %w\", subtask.ID, ErrNothingToLoad)\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"unexpected subtask status: %s\", subtask.Status)\n\t}\n\n\tmsgChains, err := taskCtx.DB.GetSubtaskPrimaryMsgChains(ctx, database.Int64ToNullInt64(&subtask.ID))\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to get subtask primary msg chains for subtask %d: %w\", subtask.ID, err)\n\t}\n\n\tif len(msgChains) == 0 {\n\t\treturn nil, fmt.Errorf(\"subtask %d has no msg chains: %w\", subtask.ID, ErrNothingToLoad)\n\t}\n\n\treturn &subtaskWorker{\n\t\tmx: &sync.RWMutex{},","sourceCodeStart":86,"sourceCodeEnd":122,"githubUrl":"https://github.com/vxcontrol/pentagi/blob/ea665308baaff015b226f308438a68d929d0f29b/backend/pkg/controller/subtask.go#L86-L122","documentation":"LoadSubtaskWorker resets a subtask stuck in a non-Created state (e.g. 'processing' after a crash) back to SubtaskStatusCreated via UpdateSubtaskStatus. This error wraps a failure of that status-reset UPDATE, identified by subtask ID. It indicates the resume/recovery path could not normalize the subtask state.","triggerScenarios":"Calling LoadSubtasks after restart for a subtask whose status was e.g. 'processing' when the DB UPDATE fails — DB down, row locked, row deleted concurrently, or constraint violation.","commonSituations":"Server crash left subtasks in 'processing'; on recovery the database is temporarily unavailable; concurrent workers race to reload the same subtask causing lock contention; migrations not applied so the status column value differs.","solutions":["Check DB connectivity and retry LoadSubtasks once the database is reachable.","Inspect the wrapped cause for lock timeouts (deadlocks/row locks) and reduce concurrent reload of the same subtask.","Verify the subtask row still exists; if deleted, remove it from the load queue instead of retrying.","Ensure all goose migrations ran so status enums/columns match the code's expectations."],"exampleFix":"// before\nworkers, err := LoadSubtasks(ctx, taskCtx)\nif err != nil {\n    return err\n}\n// after\nworkers, err := LoadSubtasks(ctx, taskCtx)\nif err != nil {\n    if isRetryableDBError(err) {\n        time.Sleep(backoff)\n        workers, err = LoadSubtasks(ctx, taskCtx)\n    }\n    if err != nil {\n        return fmt.Errorf(\"load subtasks after restart: %w\", err)\n    }\n}","handlingStrategy":"retry","validationCode":"// before recovery load, confirm the row is updatable and DB reachable\nif err := db.PingContext(ctx); err != nil {\n    return fmt.Errorf(\"db unavailable: %w\", err)\n}\nvar st string\nerr := db.QueryRowContext(ctx, `SELECT status FROM subtasks WHERE id=$1`, id).Scan(&st)\nif err != nil { return err }","typeGuard":"func IsStatusResetFailure(err error) bool {\n    return strings.Contains(err.Error(), \"status to created\")\n}","tryCatchPattern":"worker, err := LoadSubtaskWorker(ctx, taskCtx, subtask)\nif err != nil {\n    if isTransientDBError(err) {\n        return retryWithBackoff(ctx, func() error {\n            _, err = LoadSubtaskWorker(ctx, taskCtx, subtask)\n            return err\n        })\n    }\n    return err\n}","preventionTips":["Run recovery loads only after the DB connection pool is healthy.","Avoid concurrent recovery of the same subtask; use a single recovery goroutine or lease.","Tune lock_timeout/statement_timeout for status UPDATEs.","Apply all goose migrations before starting recovery."],"tags":["database","state-recovery","subtask"],"backgroundTag":"database-update-failed","analyzedSha":"ea665308baaff015b226f308438a68d929d0f29b","analyzedAt":"2026-09-01T14:16:31.421Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}