{"record":{"id":"e2395c35dafb97cc","repo":"vxcontrol/pentagi","slug":"subtask-d-has-no-msg-chains-w","errorCode":null,"errorMessage":"subtask %d has no msg chains: %w","messagePattern":"subtask (.+?) has no msg chains: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"backend/pkg/controller/subtask.go","lineNumber":118,"sourceCode":"\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{},\n\t\tsubtaskCtx: &SubtaskContext{\n\t\t\tMsgChainID:         msgChains[0].ID,\n\t\t\tSubtaskID:          subtask.ID,\n\t\t\tSubtaskTitle:       subtask.Title,\n\t\t\tSubtaskDescription: subtask.Description,\n\t\t\tTaskContext:        *taskCtx,\n\t\t},\n\t\tupdater:   updater,\n\t\tcompleted: completed,\n\t\twaiting:   waiting,\n\t}, nil\n}\n\nfunc (stw *subtaskWorker) GetMsgChainID() int64 {","sourceCodeStart":100,"sourceCodeEnd":136,"githubUrl":"https://github.com/vxcontrol/pentagi/blob/ea665308baaff015b226f308438a68d929d0f29b/backend/pkg/controller/subtask.go#L100-L136","documentation":"LoadSubtaskWorker requires at least one primary message chain to build a subtaskWorker; when GetSubtaskPrimaryMsgChains returns zero rows it wraps ErrNothingToLoad with the subtask ID. This signals the subtask has no conversation history to resume — the chain was never created or was deleted.","triggerScenarios":"A subtask reached a non-initial status but has no primary msg chain rows — e.g. chain creation failed earlier (see error 142) yet the status advanced, or chains were deleted by cleanup while the subtask row remained.","commonSituations":"Partial failure between chain creation and status update; manual DB cleanup removing msg_chain rows; recovery scan picking up orphaned subtasks; bug in chain-per-subtask bookkeeping.","solutions":["Check errors.Is(err, ErrNothingToLoad) and skip or re-initialize the subtask's chain instead of failing the batch.","Verify chains were actually created for the subtask (query msg_chain by subtask ID).","If chains were deleted intentionally, mark the subtask failed/finished rather than leaving it loadable.","Fix ordering so a subtask's status only advances after its primary chain exists."],"exampleFix":"// before\nworker, err := LoadSubtaskWorker(ctx, taskCtx, subtask)\nif err != nil {\n    return err\n}\n// after\nworker, err := LoadSubtaskWorker(ctx, taskCtx, subtask)\nif err != nil {\n    if errors.Is(err, ErrNothingToLoad) {\n        _ = taskCtx.DB.UpdateSubtaskStatus(ctx, database.UpdateSubtaskStatusParams{\n            Status: database.SubtaskStatusFailed, ID: subtask.ID,\n        })\n        return nil\n    }\n    return err\n}","handlingStrategy":"type-guard","validationCode":"// confirm at least one primary chain exists before loading\nvar n int\nerr := db.QueryRowContext(ctx,\n    `SELECT COUNT(*) FROM msg_chain WHERE subtask_id=$1`, subtaskID).Scan(&n)\nif err == nil && n == 0 {\n    return fmt.Errorf(\"subtask %d has no chains; mark failed instead\", subtaskID)\n}","typeGuard":"func IsNoMsgChains(err error) bool {\n    return errors.Is(err, controller.ErrNothingToLoad) &&\n        strings.Contains(err.Error(), \"no msg chains\")\n}","tryCatchPattern":"worker, err := LoadSubtaskWorker(ctx, taskCtx, subtask)\nif err != nil {\n    if errors.Is(err, controller.ErrNothingToLoad) {\n        _ = db.UpdateSubtaskStatus(ctx, database.UpdateSubtaskStatusParams{\n            Status: database.SubtaskStatusFailed, ID: subtask.ID})\n        return nil\n    }\n    return err\n}","preventionTips":["Only advance a subtask's status after its primary msg chain is persisted (transactional create).","Do not delete msg_chain rows while the owning subtask is still active.","Treat ErrNothingToLoad as 'skip or finalize', never as a crash.","Periodically scan for orphaned subtasks without chains and finalize them."],"tags":["msg-chain","subtask","nothing-to-load","data-consistency"],"backgroundTag":"missing-related-record","analyzedSha":"ea665308baaff015b226f308438a68d929d0f29b","analyzedAt":"2026-09-01T14:16:31.421Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}