{"record":{"id":"0b9351903163511a","repo":"vxcontrol/pentagi","slug":"subtask-not-found","errorCode":null,"errorMessage":"subtask not found","messagePattern":"subtask not found","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"backend/pkg/controller/subtasks.go","lineNumber":191,"sourceCode":"\tsubtasks := make([]SubtaskWorker, 0)\n\tfor _, subtask := range stc.subtasks {\n\t\tsubtasks = append(subtasks, subtask)\n\t}\n\n\tsort.Slice(subtasks, func(i, j int) bool {\n\t\treturn subtasks[i].GetSubtaskID() < subtasks[j].GetSubtaskID()\n\t})\n\n\treturn subtasks\n}\n\nfunc (stc *subtaskController) GetSubtask(ctx context.Context, subtaskID int64) (SubtaskWorker, error) {\n\tstc.mx.Lock()\n\tdefer stc.mx.Unlock()\n\n\tsubtask, ok := stc.subtasks[subtaskID]\n\tif !ok {\n\t\treturn nil, fmt.Errorf(\"subtask not found\")\n\t}\n\n\treturn subtask, nil\n}\n\n// InvalidateSubtasks drops workers deleted outside this controller,\n// preventing stale cache lookups.\nfunc (stc *subtaskController) InvalidateSubtasks(subtaskIDs []int64) {\n\tif len(subtaskIDs) == 0 {\n\t\treturn\n\t}\n\n\tstc.mx.Lock()\n\tdefer stc.mx.Unlock()\n\n\tfor _, id := range subtaskIDs {\n\t\tdelete(stc.subtasks, id)\n\t}","sourceCodeStart":173,"sourceCodeEnd":209,"githubUrl":"https://github.com/vxcontrol/pentagi/blob/ea665308baaff015b226f308438a68d929d0f29b/backend/pkg/controller/subtasks.go#L173-L209","documentation":"GetSubtask looks up an in-memory SubtaskWorker by ID in the controller's map. Unlike PopSubtask, it only finds workers already created in this process — it does not load from the database. This sentinel-style error means no live worker exists for that ID.","triggerScenarios":"Calling GetSubtask with an ID never popped in this process, after backend restart (map is memory-only), after InvalidateSubtasks dropped the worker (subtask deleted), or with a wrong/foreign subtask ID.","commonSituations":"Client UI asking for a subtask from a previous run after backend restart; race where a subtask was deleted while a status request was in flight; passing a subtask ID from a different task.","solutions":["Verify the subtask belongs to the current task and process instance","Use PopSubtask first to create/cache the worker before GetSubtask","Treat this as 404 at the API layer, not a 500","If it appears after restart, re-derive worker state from the DB instead of memory"],"exampleFix":"// before\nsubtask, err := stc.GetSubtask(ctx, subtaskID)\nif err != nil {\n    return err // surfaces as 500\n}\n// after\nsubtask, err := stc.GetSubtask(ctx, subtaskID)\nif err != nil {\n    if err.Error() == \"subtask not found\" {\n        return nil, ErrNotFound // map to 404\n    }\n    return nil, err\n}","handlingStrategy":"type-guard","validationCode":"func workerExists(stc *subtaskController, id int64) bool {\n    stc.mx.Lock(); defer stc.mx.Unlock()\n    _, ok := stc.subtasks[id]\n    return ok\n}","typeGuard":"func isSubtaskNotFound(err error) bool {\n    return err != nil && strings.Contains(err.Error(), \"subtask not found\")\n}","tryCatchPattern":"subtask, err := stc.GetSubtask(ctx, subtaskID)\nif err != nil {\n    if isSubtaskNotFound(err) { return ErrNotFound /* map to 404 */ }\n    return err\n}","preventionTips":["Call PopSubtask before GetSubtask to ensure the worker is cached","Remember worker state is in-memory: re-create it after restarts","Invalidate UI subscriptions when a subtask is deleted"],"tags":["not-found","in-memory-state","worker"],"backgroundTag":"entity-not-found","analyzedSha":"ea665308baaff015b226f308438a68d929d0f29b","analyzedAt":"2026-09-01T14:16:31.421Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}