goharbor/harbor · error

empty action

Error message

empty action

What it means

Returned by the retention action index Get when the action string is empty. Retention actions (e.g. retain) are registered at init time into a sync.Map keyed by action name; Get first rejects the empty key before lookup. It signals a malformed retention policy payload where the rule's action field is missing.

Source

Thrown at src/pkg/retention/policy/action/index/index.go:45

func init() {
	// Register retain action
	Register(action.Retain, action.NewRetainAction)
}

// Register the performer with the corresponding action
func Register(action string, factory action.PerformerFactory) {
	if len(action) == 0 || factory == nil {
		// do nothing
		return
	}

	index.Store(action, factory)
}

// Get performer with the provided action
func Get(act string, params any, isDryRun bool) (action.Performer, error) {
	if len(act) == 0 {
		return nil, errors.New("empty action")
	}

	v, ok := index.Load(act)
	if !ok {
		return nil, errors.Errorf("action %s is not registered", act)
	}

	factory, ok := v.(action.PerformerFactory)
	if !ok {
		return nil, errors.Errorf("invalid action performer registered for action %s", act)
	}

	return factory(params, isDryRun), nil
}

View on GitHub (pinned to 7b2fd08cc5)

Solutions

  1. Set the action field on every rule in the retention policy payload (only 'retain' is a valid action in current Harbor)
  2. Validate the policy JSON before submission, e.g. check rule.action is non-empty for each rule
  3. If generating policies in code, assert on required fields before calling the retention API

Example fix

// before
rule := map[string]any{"template": "always"}
_, err := action.Get("", params, false)

// after
rule := map[string]any{"action": "retain", "template": "always"}
_, err := action.Get("retain", params, false)
Defensive patterns

Strategy: validation

Validate before calling

for _, r := range policy.Rules {
    if len(r.Action) == 0 {
        return fmt.Errorf("retention rule is missing an action")
    }
}
_, err := actionindex.Get(policy.Rules[0].Action, params, dryRun)

Prevention

When it happens

Trigger: Submitting a retention policy whose rule JSON omits the action field; programmatically building a policy model with an unset action and passing it to policy evaluation; API POST /retentions with an empty action in the rules array.

Common situations: Hand-crafted policy JSON from scripts or Terraform providers missing the action key; UI/API payload truncated during upgrade; copy-paste of policy templates between Harbor versions with schema changes.

Related errors


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