{"record":{"id":"4fe4ba97c16fd4f1","repo":"bytebase/bytebase","slug":"plan-d-not-found","errorCode":null,"errorMessage":"plan %d not found","messagePattern":"plan (.+?) not found","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"backend/runner/taskrun/running_scheduler.go","lineNumber":303,"sourceCode":"\t}\n\n\tdatabase, err := s.store.GetDatabase(ctx, &store.FindDatabaseMessage{\n\t\tInstanceID:   &task.InstanceID,\n\t\tDatabaseName: task.DatabaseName,\n\t})\n\tif err != nil {\n\t\treturn errors.Wrapf(err, \"failed to get database for drift validation\")\n\t}\n\tif database == nil {\n\t\treturn errors.Errorf(\"target database %q on instance %q has been deleted\", task.GetDatabaseName(), task.InstanceID)\n\t}\n\n\tplan, err := s.store.GetPlan(ctx, &store.FindPlanMessage{ProjectID: task.ProjectID, UID: &task.PlanID})\n\tif err != nil {\n\t\treturn errors.Wrapf(err, \"failed to get plan for drift validation\")\n\t}\n\tif plan == nil {\n\t\treturn errors.Errorf(\"plan %d not found\", task.PlanID)\n\t}\n\n\treturn checkTaskDrift(task, database, plan)\n}\n\n// checkTaskDrift performs the actual drift comparison logic.\n// Returns nil if no drift, or an error describing the drift.\nfunc checkTaskDrift(task *store.TaskMessage, database *store.DatabaseMessage, plan *store.PlanMessage) error {\n\t// Check project drift.\n\tif database.ProjectID != plan.ProjectID {\n\t\treturn errors.Errorf(\"target database belongs to project %q, but plan belongs to project %q\", database.ProjectID, plan.ProjectID)\n\t}\n\n\t// Check environment drift.\n\tif task.Environment != \"\" && database.EffectiveEnvironmentID != nil && *database.EffectiveEnvironmentID != task.Environment {\n\t\treturn errors.Errorf(\"target database is in environment %q, but task expected environment %q\", *database.EffectiveEnvironmentID, task.Environment)\n\t}\n","sourceCodeStart":285,"sourceCodeEnd":321,"githubUrl":"https://github.com/bytebase/bytebase/blob/1870550677fe08f0d2a78c07acd27541464eb945/backend/runner/taskrun/running_scheduler.go#L285-L321","documentation":"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.","triggerScenarios":"Plan deleted (directly or via project/pipeline cleanup) after the task was created but before its run started; validateTaskFreshness sees plan == nil.","commonSituations":"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.","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"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"plan, err := store.GetPlan(ctx, &store.FindPlanMessage{ProjectID: task.ProjectID, UID: &task.PlanID})\nif err != nil { return err }\nif plan == nil { return fmt.Errorf(\"plan %d missing\", task.PlanID) }","typeGuard":"func planPresent(plan *store.PlanMessage) bool { return plan != nil }","tryCatchPattern":"if err := s.validateTaskFreshness(ctx, task); err != nil {\n\tif strings.Contains(err.Error(), \"plan \") && strings.Contains(err.Error(), \"not found\") {\n\t\treturn failTaskRun(task, \"orphan task: plan removed\")\n\t}\n\treturn err\n}","preventionTips":["Cascading-cancel tasks when their plan is deleted","Check plan existence before approving pipelines","Clean up orphan tasks after plan/project cleanup operations"],"tags":["plan","not-found","drift","scheduler"],"backgroundTag":"resource-not-found","analyzedSha":"1870550677fe08f0d2a78c07acd27541464eb945","analyzedAt":"2026-09-06T21:16:13.665Z","contentChangedAt":"2026-09-06T21:16:13.665Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}