thanos-io/thanos · error · ApiError
ID cannot be empty
Error message
ID cannot be empty
What it means
markBlock requires the form parameter 'id' identifying the block ULID. When the request omits it (or it is an empty string), the API rejects the request with an ErrorBadData ApiError 'ID cannot be empty'.
Solutions
- Send the id form field with the block ULID: curl -X POST -d 'id=01ARZ3NDEKTSV4RRFFQ69G5FAV' ... .
- Verify the upstream step that produces the block ID actually returned a value before calling the API.
- Add client-side validation that id is a non-empty ULID before issuing the request.
Example fix
// before curl -X POST http://thanos:9090/api/v1/blocks/mark -d 'action=deletion' // after curl -X POST http://thanos:9090/api/v1/blocks/mark -d 'action=deletion' -d 'id=01ARZ3NDEKTSV4RRFFQ69G5FAV'
Defensive patterns
Strategy: validation
Validate before calling
if blockID == "" {
return errors.New("cannot call /blocks/mark without a block ULID id")
}
if _, err := ulid.Parse(blockID); err != nil {
return fmt.Errorf("invalid ULID %q: %w", blockID, err)
} Prevention
- Assert the block ID exists (list blocks first) before marking
- Use ULID parsing client-side to catch malformed IDs early
- Never build form bodies from possibly-empty variables without checks
When it happens
Trigger: POST /api/v1/blocks/mark without the id form field, or with id= (empty value).
Common situations: curl calls missing -d 'id=...'; automation building the form body with an empty variable because the block ID lookup failed upstream.
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
- Action cannot be empty
- invalid targets parameter state=
- invalid rules parameter type=
- failed to get prometheus version
- invalid parameter ;
AI-assisted analysis of thanos-io/thanos@35b8b99117 (2026-09-07).
Data as JSON: /api/errors/334fe47aabae1e07.
Report an issue: GitHub.
Appendix: source
Thrown at pkg/api/blocks/v1.go:105
func (bapi *BlocksAPI) Register(r *route.Router, tracer opentracing.Tracer, logger log.Logger, ins extpromhttp.InstrumentationMiddleware, logMiddleware *logging.HTTPServerMiddleware) {
bapi.baseAPI.Register(r, tracer, logger, ins, logMiddleware)
instr := api.GetInstr(tracer, logger, ins, logMiddleware, bapi.disableCORS)
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() {}
}View on GitHub (pinned to 35b8b99117)