thanos-io/thanos · warning · ApiError

Admin operations are disabled

Error message

Admin operations are disabled

What it means

The Blocks API's markBlock endpoint (POST /blocks/mark) refuses to run when the API was started with admin operations disabled (--admin.disable). Block mark/delete operations are guarded by this flag as a safety measure.

Solutions

  1. Restart the Thanos component without the --admin.disable flag if admin operations are intended.
  2. Perform block marking via the object store tooling (thanos tools bucket mark) instead of the API.
  3. Point the automation at an instance that has admin operations enabled.

Example fix

// before
thanos sidecar --prometheus.url=http://localhost:9090 --admin.disable
// after
thanos sidecar --prometheus.url=http://localhost:9090   # admin ops enabled (or use `thanos tools bucket mark`)
Defensive patterns

Strategy: validation

Validate before calling

// probe admin availability first
resp, _ := http.Post(baseURL+"/api/v1/blocks/mark", "application/x-www-form-urlencoded", strings.NewReader("id=test&action=deletion"))
if resp != nil && resp.StatusCode == 400 { /* admin likely disabled; use bucket tooling */ }

Try / catch

resp, err := http.PostForm(url, vals)
if err != nil { return err }
var e struct{ Error string `json:"error"`; Status string `json:"status"` }
json.NewDecoder(resp.Body).Decode(&e)
if e.Status == "error" && strings.Contains(e.Error, "Admin operations are disabled") {
    return fmt.Errorf("admin ops disabled on this instance; use `thanos tools bucket mark` instead")
}

Prevention

When it happens

Trigger: POST /api/v1/blocks/mark while the blocks API server was constructed with disableAdminOperations=true (Thanos started with --admin.disable).

Common situations: Production deployments launched with --admin.disable attempting block metadata marking via API; automation tooling calling mark endpoints against a read-only store gateway/UI instance.

Related errors


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

Appendix: source

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

		},
		disableCORS:            disableCORS,
		bkt:                    bkt,
		disableAdminOperations: disableAdminOperations,
	}
}

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() {}
	}

View on GitHub (pinned to 35b8b99117)