vxcontrol/pentagi · error
subtask not found
Error message
subtask not found
What it means
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.
Source
Thrown at backend/pkg/controller/subtasks.go:191
subtasks := make([]SubtaskWorker, 0)
for _, subtask := range stc.subtasks {
subtasks = append(subtasks, subtask)
}
sort.Slice(subtasks, func(i, j int) bool {
return subtasks[i].GetSubtaskID() < subtasks[j].GetSubtaskID()
})
return subtasks
}
func (stc *subtaskController) GetSubtask(ctx context.Context, subtaskID int64) (SubtaskWorker, error) {
stc.mx.Lock()
defer stc.mx.Unlock()
subtask, ok := stc.subtasks[subtaskID]
if !ok {
return nil, fmt.Errorf("subtask not found")
}
return subtask, nil
}
// InvalidateSubtasks drops workers deleted outside this controller,
// preventing stale cache lookups.
func (stc *subtaskController) InvalidateSubtasks(subtaskIDs []int64) {
if len(subtaskIDs) == 0 {
return
}
stc.mx.Lock()
defer stc.mx.Unlock()
for _, id := range subtaskIDs {
delete(stc.subtasks, id)
}View on GitHub (pinned to ea665308ba)
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
Example fix
// before
subtask, err := stc.GetSubtask(ctx, subtaskID)
if err != nil {
return err // surfaces as 500
}
// after
subtask, err := stc.GetSubtask(ctx, subtaskID)
if err != nil {
if err.Error() == "subtask not found" {
return nil, ErrNotFound // map to 404
}
return nil, err
} Defensive patterns
Strategy: type-guard
Validate before calling
func workerExists(stc *subtaskController, id int64) bool {
stc.mx.Lock(); defer stc.mx.Unlock()
_, ok := stc.subtasks[id]
return ok
} Type guard
func isSubtaskNotFound(err error) bool {
return err != nil && strings.Contains(err.Error(), "subtask not found")
} Try / catch
subtask, err := stc.GetSubtask(ctx, subtaskID)
if err != nil {
if isSubtaskNotFound(err) { return ErrNotFound /* map to 404 */ }
return err
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: "Not found" and "does not exist" errors: why "Task not found", "No such folder", and "Can't find" fire when a lookup comes back empty — this error's family across 14 libraries.
Related errors
AI-assisted analysis of vxcontrol/pentagi@ea665308ba (2026-09-01).
Data as JSON: /api/errors/0b9351903163511a.
Report an issue: GitHub.