goharbor/harbor · warning · lib/errors.Error
BAD_REQUEST
BAD_REQUEST
Error message
require digest
What it means
The blob controller's Get(ctx, digest) uses the digest string as the only lookup key (it builds an OrList on keywords["digest"]). An empty digest cannot identify any blob record, so the controller rejects the call with BAD_REQUEST before it reaches the DAO layer. This is a guard against callers forwarding a missing/zero-value digest.
Source
Thrown at src/controller/blob/controller.go:223
associated := make(map[string]bool, len(associatedBlobs))
for _, blob := range associatedBlobs {
associated[blob.Digest] = true
}
var results []*blob.Blob
for _, blob := range blobs {
if !associated[blob.Digest] {
results = append(results, blob)
}
}
return results, nil
}
func (c *controller) Get(ctx context.Context, digest string, options ...Option) (*blob.Blob, error) {
if digest == "" {
return nil, errors.New(nil).WithCode(errors.BadRequestCode).WithMessage("require digest")
}
opts := newOptions(options...)
keywords := make(map[string]any)
if digest != "" {
ol := q.OrList{
Values: []any{
digest,
},
}
keywords["digest"] = &ol
}
if opts.ProjectID != 0 {
keywords["projectID"] = opts.ProjectID
}
if opts.ArtifactDigest != "" {
keywords["artifactDigest"] = opts.ArtifactDigestView on GitHub (pinned to 7b2fd08cc5)
Solutions
- Pass the non-empty sha256 digest of the blob you want to fetch.
- Validate the digest at the boundary (route handler / API client) before calling the controller, ideally with the standard digest regexp.
- If the digest comes from another subsystem, debug why it arrives empty (nil manifest, truncated response) instead of retrying unchanged.
Example fix
// before
b, err := blobCtl.Get(ctx, digest) // digest == ""
// after
if digest == "" {
return liberrors.BadRequestError(errors.New("require digest"))
}
b, err := blobCtl.Get(ctx, digest) Defensive patterns
Strategy: validation
Validate before calling
var digestRe = regexp.MustCompile(`^sha256:[a-fA-F0-9]{64}$`)
func validDigest(d string) bool { return digestRe.MatchString(d) }
// call before blobCtl.Get:
if !validDigest(digest) {
return liberrors.BadRequestError(fmt.Errorf("require digest"))
} Type guard
func isDigestable(s string) bool {
return len(s) > len("sha256:") && strings.HasPrefix(s, "sha256:")
} Try / catch
if _, err := blobCtl.Get(ctx, digest); err != nil {
if liberrors.IsErr(err, liberrors.BadRequestCode) && err.Error() == "require digest" {
// caller bug: fix the digest source, do not retry
}
return err
} Prevention
- Validate digest format at the API boundary with the standard regexp.
- Never pass struct fields straight through - assert non-empty first.
- Fail fast in route handlers for missing :digest path params.
When it happens
Trigger: Calling controller/blob Get with digest == "" - e.g. an API handler that forwards a missing :digest path/query parameter, or internal code (replication, scanning, GC) that reads a digest field that was never populated.
Common situations: Client calls a blob endpoint without the digest segment; upstream struct has an empty Digest because the manifest was fetched partially; code assumes an earlier layer already validated the digest.
Related errors
- 10013
- File {} not exist
- Internal dir for tls {} not exist
- Port number in metrics is not valid
- Jaeger Colector Endpoint or Agent host not set, must set one
AI-assisted analysis of goharbor/harbor@7b2fd08cc5 (2026-08-16).
Data as JSON: /api/errors/6b2423d525bb55dd.
Report an issue: GitHub.