bytebase/bytebase · error
plan %d not found
Error message
plan %d not found
What it means
Thrown when GetPlan returns (nil, nil): the plan associated with the task no longer exists. Drift validation requires the plan to compare the task's SQL against the planned schema, so the task run is refused with this message naming the plan ID.
Source
Thrown at backend/runner/taskrun/running_scheduler.go:303
}
database, err := s.store.GetDatabase(ctx, &store.FindDatabaseMessage{
InstanceID: &task.InstanceID,
DatabaseName: task.DatabaseName,
})
if err != nil {
return errors.Wrapf(err, "failed to get database for drift validation")
}
if database == nil {
return errors.Errorf("target database %q on instance %q has been deleted", task.GetDatabaseName(), task.InstanceID)
}
plan, err := s.store.GetPlan(ctx, &store.FindPlanMessage{ProjectID: task.ProjectID, UID: &task.PlanID})
if err != nil {
return errors.Wrapf(err, "failed to get plan for drift validation")
}
if plan == nil {
return errors.Errorf("plan %d not found", task.PlanID)
}
return checkTaskDrift(task, database, plan)
}
// checkTaskDrift performs the actual drift comparison logic.
// Returns nil if no drift, or an error describing the drift.
func checkTaskDrift(task *store.TaskMessage, database *store.DatabaseMessage, plan *store.PlanMessage) error {
// Check project drift.
if database.ProjectID != plan.ProjectID {
return errors.Errorf("target database belongs to project %q, but plan belongs to project %q", database.ProjectID, plan.ProjectID)
}
// Check environment drift.
if task.Environment != "" && database.EffectiveEnvironmentID != nil && *database.EffectiveEnvironmentID != task.Environment {
return errors.Errorf("target database is in environment %q, but task expected environment %q", *database.EffectiveEnvironmentID, task.Environment)
}
View on GitHub (pinned to 1870550677)
Solutions
- Cancel the orphan task and create a fresh plan/task pair
- Restore the plan if deletion was accidental
- Check task.PlanID against existing plans in the project
- Prevent deleting plans that have pending task runs
Defensive patterns
Strategy: validation
Validate before calling
plan, err := store.GetPlan(ctx, &store.FindPlanMessage{ProjectID: task.ProjectID, UID: &task.PlanID})
if err != nil { return err }
if plan == nil { return fmt.Errorf("plan %d missing", task.PlanID) } Type guard
func planPresent(plan *store.PlanMessage) bool { return plan != nil } Try / catch
if err := s.validateTaskFreshness(ctx, task); err != nil {
if strings.Contains(err.Error(), "plan ") && strings.Contains(err.Error(), "not found") {
return failTaskRun(task, "orphan task: plan removed")
}
return err
} Prevention
- Cascading-cancel tasks when their plan is deleted
- Check plan existence before approving pipelines
- Clean up orphan tasks after plan/project cleanup operations
When it happens
Trigger: Plan deleted (directly or via project/pipeline cleanup) after the task was created but before its run started; validateTaskFreshness sees plan == nil.
Common situations: Plan removed during pipeline re-planning while an old task was still queued; data cleanup purging old plans; import/migration of projects leaving orphan tasks.
Understand the failure class
Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.
Related errors
AI-assisted analysis of bytebase/bytebase@1870550677 (2026-09-06).
Data as JSON: /api/errors/4fe4ba97c16fd4f1.
Report an issue: GitHub.