vxcontrol/pentagi · error

unexpected subtask status: %s

Error message

unexpected subtask status: %s

What it means

LoadSubtaskWorker hits its default switch branch when the subtask's status is none of the handled values (processed/created handling above), i.e. an unrecognized or intentionally un-settable status (such as 'created' set via this path or a corrupted value). It returns the raw status string for diagnosis.

Source

Thrown at backend/pkg/controller/subtask.go:109

	case database.SubtaskStatusFinished, database.SubtaskStatusFailed:
		completed = true
	case database.SubtaskStatusWaiting:
		waiting = true
	case database.SubtaskStatusRunning:
		var err error
		// if subtask is running, it means that it was not finished by previous run
		// so we need to set it to created and continue from the beginning
		subtask, err = taskCtx.DB.UpdateSubtaskStatus(ctx, database.UpdateSubtaskStatusParams{
			Status: database.SubtaskStatusCreated,
			ID:     subtask.ID,
		})
		if err != nil {
			return nil, fmt.Errorf("failed to update subtask %d status to created: %w", subtask.ID, err)
		}
	case database.SubtaskStatusCreated:
		return nil, fmt.Errorf("subtask %d has created yet: %w", subtask.ID, ErrNothingToLoad)
	default:
		return nil, fmt.Errorf("unexpected subtask status: %s", subtask.Status)
	}

	msgChains, err := taskCtx.DB.GetSubtaskPrimaryMsgChains(ctx, database.Int64ToNullInt64(&subtask.ID))
	if err != nil {
		return nil, fmt.Errorf("failed to get subtask primary msg chains for subtask %d: %w", subtask.ID, err)
	}

	if len(msgChains) == 0 {
		return nil, fmt.Errorf("subtask %d has no msg chains: %w", subtask.ID, ErrNothingToLoad)
	}

	return &subtaskWorker{
		mx: &sync.RWMutex{},
		subtaskCtx: &SubtaskContext{
			MsgChainID:         msgChains[0].ID,
			SubtaskID:          subtask.ID,
			SubtaskTitle:       subtask.Title,
			SubtaskDescription: subtask.Description,

View on GitHub (pinned to ea665308ba)

Solutions

  1. Log the subtask ID and status value; confirm what the actual stored status is.
  2. Filter out terminal-status subtasks (finished/failed) before calling LoadSubtaskWorker.
  3. After a version upgrade, run all goose migrations and verify status enum values match database.SubtaskStatus* constants.
  4. Add an explicit case for any newly introduced status in the switch.

Example fix

// before
subtasks := taskCtx.DB.GetAllSubtasks(ctx)
for _, s := range subtasks {
    LoadSubtaskWorker(ctx, taskCtx, s)
}
// after
subtasks := taskCtx.DB.GetAllSubtasks(ctx)
for _, s := range subtasks {
    if s.Status == database.SubtaskStatusFinished || s.Status == database.SubtaskStatusFailed {
        continue // skip terminal subtasks
    }
    LoadSubtaskWorker(ctx, taskCtx, s)
}
Defensive patterns

Strategy: validation

Validate before calling

switch subtask.Status {
case database.SubtaskStatusProcessed, database.SubtaskStatusProcessing:
    // ok to load
default:
    return fmt.Errorf("skip subtask %d: unsupported status %q", subtask.ID, subtask.Status)
}

Type guard

func IsLoadableStatus(s database.SubtaskStatus) bool {
    switch s {
    case database.SubtaskStatusProcessed, database.SubtaskStatusProcessing:
        return true
    }
    return false
}

Try / catch

if !IsLoadableStatus(subtask.Status) {
    log.Warn("unexpected subtask status", "id", subtask.ID, "status", subtask.Status)
    return nil // skip rather than propagate
}
worker, err := LoadSubtaskWorker(ctx, taskCtx, subtask)
if err != nil { return err }

Prevention

When it happens

Trigger: A subtask row has a status value outside the enumerated cases in LoadSubtaskWorker — e.g. status 'failed'/'finished' passed to a loader, or a status string from an older schema version not matching current constants.

Common situations: Loading subtasks after a version upgrade where the status enum changed; DB rows manually edited; a bug elsewhere set an unexpected status; passing finished subtasks through the resume path.

Related errors


AI-assisted analysis of vxcontrol/pentagi@ea665308ba (2026-09-01). Data as JSON: /api/errors/fb50b22c0e346ccf. Report an issue: GitHub.