goharbor/harbor · error

malformed endpoint

Error message

malformed endpoint

What it means

Returned by scanner Registration.Validate when checkUUID is true and the registration's UUID is empty. Despite the 'malformed endpoint' wording, the guard actually checks the UUID: it exists so update paths (which must reference an existing registration) fail fast when the UUID was not set.

Source

Thrown at src/pkg/scan/dao/scanner/model.go:102

	}

	return json.Unmarshal([]byte(jsonData), r)
}

// ToJSON marshals registration to JSON data
func (r *Registration) ToJSON() (string, error) {
	data, err := json.Marshal(r)
	if err != nil {
		return "", err
	}

	return string(data), nil
}

// Validate registration
func (r *Registration) Validate(checkUUID bool) error {
	if checkUUID && len(r.UUID) == 0 {
		return errors.New("malformed endpoint")
	}

	if len(r.Name) == 0 {
		return errors.New("missing registration name")
	}

	url, err := lib.ValidateHTTPURL(r.URL)
	if err != nil {
		return errors.Wrap(err, "scanner registration validate")
	}
	r.URL = url

	if len(r.Auth) > 0 &&
		r.Auth != auth.Basic &&
		r.Auth != auth.Bearer &&
		r.Auth != auth.APIKey {
		return errors.Errorf("auth type %s is not supported", r.Auth)
	}

View on GitHub (pinned to 7b2fd08cc5)

Solutions

  1. Set r.UUID from the request path/payload before validating on update flows
  2. For creation flows, call Validate(false) so the UUID guard is skipped
  3. Ignore the message wording: it is the UUID that is missing, not the endpoint URL

Example fix

// before
reg := parseBody(r)
err := reg.Validate(true) // UUID never set -> 'malformed endpoint'

// after
reg := parseBody(r)
reg.UUID = mux.Vars(r)["uuid"]
err := reg.Validate(true)
Defensive patterns

Strategy: validation

Validate before calling

// On update paths, bind the path UUID into the model first
if len(reg.UUID) == 0 {
    return errors.New("scanner registration uuid is required for update")
}
err := reg.Validate(true)

Type guard

func hasUUID(r *scanner.Registration) bool {
    return r != nil && len(r.UUID) > 0
}

Prevention

When it happens

Trigger: Calling Validate(true) on a Registration built without a UUID, typically before an update/delete by UUID; API PUT to /scanners/{uuid} where the payload deserialization left UUID empty; internal code reusing the Create-validation path for updates.

Common situations: Scanner registration update requests where the path UUID is not copied into the model; API clients sending the registration body without the uuid field on update; the misleading message sending developers to debug the URL instead of the UUID.

Understand the failure class

Related errors


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