goharbor/harbor · error · lib/errors.Error
PRECONDITION
PRECONDITION
Error message
Provider [%s] cannot be deleted as some preheat policies are using it
What it means
Before deleting a preheat provider instance, the controller lists preheat policies with provider_id = id. If any policy still references the instance, deletion is refused with PRECONDITION (HTTP 412) naming the instance - deleting would orphan those policies.
Source
Thrown at src/controller/p2p/preheat/controller.go:220
// DeleteInstance implements @Controller.Delete
func (c *controller) DeleteInstance(ctx context.Context, id int64) error {
ins, err := c.GetInstance(ctx, id)
if err != nil {
return err
}
// delete instance should check the instance whether be used by policies
policies, err := c.ListPolicies(ctx, &q.Query{
Keywords: map[string]any{
"provider_id": id,
},
})
if err != nil {
return err
}
if len(policies) > 0 {
return errors.New(nil).
WithCode(errors.PreconditionCode).
WithMessagef("Provider [%s] cannot be deleted as some preheat policies are using it", ins.Name)
}
return c.iManager.Delete(ctx, id)
}
// UpdateInstance implements @Controller.Update
func (c *controller) UpdateInstance(ctx context.Context, instance *providerModels.Instance, properties ...string) error {
oldIns, err := c.GetInstance(ctx, instance.ID)
if err != nil {
return err
}
if !instance.Enabled {
// update instance should check the instance whether be used by policies
policies, err := c.ListPolicies(ctx, &q.Query{
Keywords: map[string]any{View on GitHub (pinned to 7b2fd08cc5)
Solutions
- List dependent policies first (GET /api/v2/preheat/policies?project_id=... filtered by provider) and delete or point them to another instance.
- Only after the policy count is zero, retry the instance DELETE.
- If policies are obsolete, bulk-delete them before decommissioning the provider.
Example fix
# before
DELETE /api/v2/preheat/instances/dragonfly
# after
GET /api/v2/preheat/policies?project_id=1 # find policies with provider_id of the instance
DELETE /api/v2/preheat/policies/{policy_id} # repeat for each
DELETE /api/v2/preheat/instances/dragonfly Defensive patterns
Strategy: validation
Validate before calling
// count dependents before deleting an instance:
policies, err := preheatCtl.ListPolicies(ctx, q.New(q.KeyWords{"provider_id": instanceID}))
if err != nil { return err }
if len(policies) > 0 {
return fmt.Errorf("delete %d dependent policies first", len(policies))
}
return preheatCtl.DeleteInstance(ctx, instanceID) Try / catch
if err := preheatCtl.DeleteInstance(ctx, id); err != nil {
if liberrors.IsErr(err, liberrors.PreconditionCode) {
// fetch policies with provider_id=id, remove/repoint them, retry
}
return err
} Prevention
- Decommission preheat instances only after their policies are removed.
- Add a dependency check step to cleanup runbooks.
- Prefer disabling at the policy level for maintenance windows.
When it happens
Trigger: DELETE /api/v2/preheat/instances/{name} while one or more preheat policies use this instance as their provider.
Common situations: Decommissioning a Dragonfly/kraken instance without first removing its policies; shared instance retired while teams still have policies; cleanup scripts ignoring dependencies.
Related errors
AI-assisted analysis of goharbor/harbor@7b2fd08cc5 (2026-08-16).
Data as JSON: /api/errors/7b67ed98f214b6a2.
Report an issue: GitHub.