thanos-io/thanos · error · ApiError

Action cannot be empty

Error message

Action cannot be empty

What it means

markBlock requires the form parameter 'action' stating what to mark the block for (e.g. deletion, no-compact). A request with a valid id but missing/empty action is rejected with ErrorBadData 'Action cannot be empty'.

Solutions

  1. Include the action form field with a supported value, e.g. action=deletion (or no-compact) along with id and detail.
  2. Check the markBlock handler's valid actions list and pass exactly one.
  3. Validate the action client-side before calling the endpoint.

Example fix

// before
curl -X POST http://thanos:9090/api/v1/blocks/mark -d 'id=01ARZ3NDEKTSV4RRFFQ69G5FAV'
// after
curl -X POST http://thanos:9090/api/v1/blocks/mark -d 'id=01ARZ3NDEKTSV4RRFFQ69G5FAV' -d 'action=deletion' -d 'detail=marked by cleanup job'
Defensive patterns

Strategy: validation

Validate before calling

var validActions = map[string]bool{"deletion": true, "no-compact": true}
if !validActions[action] {
    return fmt.Errorf("action %q must be one of deletion|no-compact", action)
}

Prevention

When it happens

Trigger: POST /api/v1/blocks/mark with id set but no action form field (or action= empty).

Common situations: Automation supplying only the block id; refactored scripts dropping the action parameter; interactive calls assuming a default action exists.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


AI-assisted analysis of thanos-io/thanos@35b8b99117 (2026-09-07). Data as JSON: /api/errors/da1357f2169703a1. Report an issue: GitHub.

Appendix: source

Thrown at pkg/api/blocks/v1.go:109

	r.Get("/blocks", instr("blocks", bapi.blocks))
	r.Post("/blocks/mark", instr("blocks_mark", bapi.markBlock))
}

func (bapi *BlocksAPI) markBlock(r *http.Request) (any, []error, *api.ApiError, func()) {
	if bapi.disableAdminOperations {
		return nil, nil, &api.ApiError{Typ: api.ErrorBadData, Err: errors.New("Admin operations are disabled")}, func() {}
	}
	idParam := r.FormValue("id")
	actionParam := r.FormValue("action")
	detailParam := r.FormValue("detail")

	if idParam == "" {
		return nil, nil, &api.ApiError{Typ: api.ErrorBadData, Err: errors.New("ID cannot be empty")}, func() {}
	}

	if actionParam == "" {
		return nil, nil, &api.ApiError{Typ: api.ErrorBadData, Err: errors.New("Action cannot be empty")}, func() {}
	}

	id, err := ulid.Parse(idParam)
	if err != nil {
		return nil, nil, &api.ApiError{Typ: api.ErrorBadData, Err: errors.Errorf("ULID %q is not valid: %v", idParam, err)}, func() {}
	}

	actionType := parse(actionParam)
	switch actionType {
	case Deletion:
		err := block.MarkForDeletion(r.Context(), bapi.logger, bapi.bkt, id, detailParam, promauto.With(nil).NewCounter(prometheus.CounterOpts{}))
		if err != nil {
			return nil, nil, &api.ApiError{Typ: api.ErrorBadData, Err: err}, func() {}
		}
	case NoCompaction:
		err := block.MarkForNoCompact(r.Context(), bapi.logger, bapi.bkt, id, metadata.ManualNoCompactReason, detailParam, promauto.With(nil).NewCounter(prometheus.CounterOpts{}))
		if err != nil {
			return nil, nil, &api.ApiError{Typ: api.ErrorBadData, Err: err}, func() {}

View on GitHub (pinned to 35b8b99117)