goharbor/harbor · error

the metadata of resource cannot be null

Error message

the metadata of resource cannot be null

What it means

Returned by the JFrog Artifactory adapter's PrepareForPush (src/pkg/reg/adapter/jfrog/adapter.go:123) when a resource in the slice is non-nil but its Metadata field is nil. The adapter needs resource.Metadata.Repository to derive the Artifactory repository (namespace) to pre-create, so a missing Metadata struct aborts the whole push preparation.

Source

Thrown at src/pkg/reg/adapter/jfrog/adapter.go:123

}

func newAdapter(registry *model.Registry) (adp.Adapter, error) {
	return &adapter{
		Adapter:  native.NewAdapter(registry),
		registry: registry,
		client:   newClient(registry),
	}, nil
}

// PrepareForPush creates local docker repository in jfrog artifactory
func (a *adapter) PrepareForPush(resources []*model.Resource) error {
	var namespaces []string
	for _, resource := range resources {
		if resource == nil {
			return errors.New("the resource cannot be null")
		}
		if resource.Metadata == nil {
			return errors.New("the metadata of resource cannot be null")
		}
		if resource.Metadata.Repository == nil {
			return errors.New("the namespace of resource cannot be null")
		}
		if len(resource.Metadata.Repository.Name) == 0 {
			return errors.New("the name of namespace cannot be null")
		}
		path := strings.Split(resource.Metadata.Repository.Name, "/")
		if len(path) > 0 {
			namespaces = append(namespaces, path[0])
		}
	}

	repositories, err := a.listAllRepositories()
	if err != nil {
		return err
	}
	existedRepositories := make(map[string]struct{})

View on GitHub (pinned to 7b2fd08cc5)

Solutions

  1. Always populate resource.Metadata (&model.ResourceMetadata{}) when building push resources; only the nil-resource case is tolerated less strictly than this.
  2. Add a pre-flight validation pass that rejects nil Metadata with the element index (see validationCode).
  3. Check the construction site: grep for 'model.Resource{' in your code and confirm every push-path literal includes Metadata.
  4. In tests, use the same builders production code uses instead of hand-rolled literals.

Example fix

// before
res := &model.Resource{
    Type: model.ResourceTypeImage,
}

// after
res := &model.Resource{
    Type:     model.ResourceTypeImage,
    Metadata: &model.ResourceMetadata{
        Repository: &model.Repository{Name: "docker-local/ubuntu"},
    },
}
Defensive patterns

Strategy: validation

Validate before calling

for i, r := range resources {
	if r == nil || r.Metadata == nil {
		return fmt.Errorf("resources[%d]: nil resource or metadata", i)
	}
}

Type guard

func hasMetadata(r *model.Resource) bool {
	return r != nil && r.Metadata != nil
}

Try / catch

if err := a.PrepareForPush(resources); err != nil {
	if strings.Contains(err.Error(), "metadata of resource cannot be null") {
		// fix construction site: resource literals must include Metadata
	}
	return err
}

Prevention

When it happens

Trigger: Passing a *model.Resource constructed as &model.Resource{...} without the Metadata field set — e.g. only Type or Deleted flags populated — to PrepareForPush on the jfrog adapter.

Common situations: Building partial Resource objects in tests or custom copiers; code that constructs a resource for deletion (where Metadata may be omitted) and reuses the same path for push; refactors that move Metadata population into an optional branch.

Related errors


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