goharbor/harbor · error · lib/errors.Error

PRECONDITION

PRECONDITION

Error message

the registry %d is referenced by replication policies, cannot delete it

What it means

First referential check in registry Delete(): replication policies with src_registry_id = id are counted; a count > 0 refuses deletion with PRECONDITION 'the registry %d is referenced by replication policies, cannot delete it'. A registry used as a replication SOURCE cannot be removed while policies depend on it.

Source

Thrown at src/controller/registry/controller.go:142

func (c *controller) Update(ctx context.Context, registry *model.Registry, props ...string) error {
	if err := c.validate(ctx, registry); err != nil {
		return err
	}
	return c.regMgr.Update(ctx, registry, props...)
}

func (c *controller) Delete(ctx context.Context, id int64) error {
	// referenced by replication policy as source registry
	count, err := c.repMgr.Count(ctx, &q.Query{
		Keywords: map[string]any{
			"src_registry_id": id,
		},
	})
	if err != nil {
		return err
	}
	if count > 0 {
		return errors.New(nil).WithCode(errors.PreconditionCode).WithMessagef("the registry %d is referenced by replication policies, cannot delete it", id)
	}
	// referenced by replication policy as destination registry
	count, err = c.repMgr.Count(ctx, &q.Query{
		Keywords: map[string]any{
			"dest_registry_id": id,
		},
	})
	if err != nil {
		return err
	}
	if count > 0 {
		return errors.New(nil).WithCode(errors.PreconditionCode).WithMessagef("the registry %d is referenced by replication policies, cannot delete it", id)
	}
	// referenced by proxy cache project
	count, err = c.proMgr.Count(ctx, &q.Query{
		Keywords: map[string]any{
			"registry_id": id,
		},

View on GitHub (pinned to 7b2fd08cc5)

Solutions

  1. List replication policies and delete or edit those with this registry as source (filter on src_registry_id).
  2. Repeat the DELETE only when the count is zero.
  3. Communicate with policy owners before removing shared source registries.

Example fix

# before
DELETE /api/v2/registries/5

# after
GET /api/v2/replication/policies?src_registry_id=5   # find dependents
PUT  /api/v2/replication/policies/{pid}              # repoint src_registry or
DELETE /api/v2/replication/policies/{pid}            # remove policy
DELETE /api/v2/registries/5
Defensive patterns

Strategy: validation

Validate before calling

// count source references before delete:
count, err := repMgr.Count(ctx, q.New(q.KeyWords{"src_registry_id": id}))
if err != nil { return err }
if count > 0 { return fmt.Errorf("%d replication policies use registry %d as source", count, id) }
return regCtl.Delete(ctx, id)

Try / catch

if err := regCtl.Delete(ctx, id); err != nil {
    if liberrors.IsErr(err, liberrors.PreconditionCode) {
        // list policies (src_registry_id=id), delete or repoint, then retry
    }
    return err
}

Prevention

When it happens

Trigger: DELETE /api/v2/registries/{id} while replication policies pull FROM this registry (it is their src_registry).

Common situations: Decommissioning an upstream registry without checking replication rules; shared registry retired while policies remain.

Related errors


AI-assisted analysis of goharbor/harbor@7b2fd08cc5 (2026-08-16). Data as JSON: /api/errors/2e15bebc33401e8e. Report an issue: GitHub.