goharbor/harbor · error
the tag deletion isn't supported
Error message
the tag deletion isn't supported
What it means
Returned unconditionally by (*native.Adapter).DeleteTag (src/pkg/reg/adapter/native/adapter.go:245), the adapter for plain Docker Registry v2 endpoints. The Docker Distribution HTTP API has no per-tag delete endpoint (tags are mutable pointers to manifests), so the adapter declares tag deletion unsupported rather than silently doing nothing. Note the sibling DeleteManifest/CanBeMount comments: only manifest/digest operations exist for docker registry.
Source
Thrown at src/pkg/reg/adapter/native/adapter.go:245
return filter.DoFilterArtifacts(artifacts, filters)
}
// PingSimple checks whether the registry is available. It checks the connectivity and certificate (if TLS enabled)
// only, regardless of 401/403 error.
func (a *Adapter) PingSimple() error {
err := a.Ping()
if err == nil {
return nil
}
if errors.IsErr(err, errors.UnAuthorizedCode) || errors.IsErr(err, errors.ForbiddenCode) {
return nil
}
return err
}
// DeleteTag isn't supported for docker registry
func (a *Adapter) DeleteTag(_, _ string) error {
return errors.New("the tag deletion isn't supported")
}
// CanBeMount isn't supported for docker registry
func (a *Adapter) CanBeMount(_ string) (mount bool, repository string, err error) {
return false, "", nil
}
View on GitHub (pinned to 7b2fd08cc5)
Solutions
- Delete by digest instead: resolve the tag to a manifest digest and call DeleteManifest (or the registry DELETE /v2/<name>/manifests/<digest> API).
- In Harbor, delete the artifact (which removes all its tags) via the Harbor API rather than the registry adapter.
- Check the adapter's Info()/capabilities before attempting tag-level operations and skip unsupported ones.
- If per-tag removal is required, run a garbage-collect-enabled retention policy (Harbor retention rules / registry GC) instead of tag deletion.
Example fix
// before
err := adapter.DeleteTag("myrepo", "v1.0")
// always: the tag deletion isn't supported
// after
manifest, err := adapter.ListTagExist... // resolve digest, e.g. via HEAD /v2/myrepo/manifests/v1.0
digest := manifest.Digest
err := adapter.DeleteManifest("myrepo", digest.String()) Defensive patterns
Strategy: validation
Validate before calling
// docker registry (native) has no tag-delete API: gate the call
info, err := adapter.Info()
if err != nil {
return err
}
if strings.Contains(adapter.Info().Name, "docker") {
// skip DeleteTag; use digest-based deletion
}
_ = info Type guard
func supportsTagDeletion(a adp.Adapter) bool {
// native.Adapter is the docker registry v2 adapter and rejects DeleteTag
_, isNative := a.(*native.Adapter)
return !isNative
} Try / catch
if err := adapter.DeleteTag(repo, tag); err != nil {
if strings.Contains(err.Error(), "tag deletion isn't supported") {
// fall back: resolve tag -> digest, then DeleteManifest(repo, digest)
return adapter.DeleteManifest(repo, digest)
}
return err
} Prevention
- Model deletions at manifest/digest granularity for docker registry destinations.
- Check adapter type/capabilities before tag-level operations.
- Use Harbor's artifact deletion API when the destination is Harbor.
When it happens
Trigger: Invoking adapter.DeleteTag(repository, tag) on the native adapter — directly or via a replication/rule engine that calls tag deletion on a docker-registry-type destination. The method signature accepts any strings but always returns this error.
Common situations: Porting logic from Harbor (which supports tag delete via its own artifact model) to a raw docker registry destination; retention/cleanup scripts that try to delete tags on a Distribution registry; version changes where a previously worked-around path now funnels into DeleteTag.
Related errors
- File {} not exist
- invalid {}
- key file {} permission is not 600
- File {} should readable by owner
- cert file {} should include SAN
AI-assisted analysis of goharbor/harbor@7b2fd08cc5 (2026-08-16).
Data as JSON: /api/errors/480507d1f5560e06.
Report an issue: GitHub.