{"record":{"id":"7343a6e7e7c653d0","repo":"thanos-io/thanos","slug":"error-getting-tenant-from-http","errorCode":null,"errorMessage":"error getting tenant from HTTP","messagePattern":"error getting tenant from HTTP","errorType":"http","errorClass":null,"httpStatus":400,"severity":"warning","filePath":"pkg/receive/handler_otlp.go","lineNumber":31,"sourceCode":"\t\"github.com/pkg/errors\"\n\t\"github.com/prometheus/prometheus/storage/remote\"\n\t\"github.com/thanos-io/thanos/pkg/receive/otlptranslator\"\n\ttprompb \"github.com/thanos-io/thanos/pkg/store/storepb/prompb\"\n\t\"github.com/thanos-io/thanos/pkg/tenancy\"\n\t\"github.com/thanos-io/thanos/pkg/tracing\"\n\t\"go.opentelemetry.io/collector/pdata/pmetric\"\n)\n\nfunc (h *Handler) receiveOTLPHTTP(w http.ResponseWriter, r *http.Request) {\n\tvar err error\n\tspan, ctx := tracing.StartSpan(r.Context(), \"receive_otlp_http\")\n\tspan.SetTag(\"receiver.mode\", string(h.receiverMode))\n\tdefer span.Finish()\n\n\ttenant, err := tenancy.GetTenantFromHTTP(r, h.options.TenantHeader, h.options.DefaultTenantID, h.options.TenantField)\n\tif err != nil {\n\t\tlevel.Error(h.logger).Log(\"msg\", \"error getting tenant from HTTP\", \"err\", err)\n\t\thttp.Error(w, err.Error(), http.StatusBadRequest)\n\t\treturn\n\t}\n\n\ttLogger := log.With(h.logger, \"tenant\", tenant)\n\tspan.SetTag(\"tenant\", tenant)\n\n\twriteGate := h.Limiter.WriteGate()\n\ttracing.DoInSpan(r.Context(), \"receive_write_gate_ismyturn\", func(ctx context.Context) {\n\t\terr = writeGate.Start(r.Context())\n\t})\n\n\tdefer writeGate.Done()\n\tif err != nil {\n\t\tlevel.Error(tLogger).Log(\"err\", err, \"msg\", \"internal server error\")\n\t\thttp.Error(w, err.Error(), http.StatusInternalServerError)\n\t\treturn\n\t}\n","sourceCodeStart":13,"sourceCodeEnd":49,"githubUrl":"https://github.com/thanos-io/thanos/blob/35b8b991177def87ed52dcf10f9b6d87f07282c8/pkg/receive/handler_otlp.go#L13-L49","documentation":"This is the log message (and HTTP 400 response) emitted by Handler.receiveOTLPHTTP when tenancy.GetTenantFromHTTP fails to resolve a tenant from the incoming OTLP/HTTP request. Thanos receive requires a tenant identity (via the tenant header, default tenant ID, or tenant field) to apply limits and store data; if extraction fails the request is rejected as a client error. The error message logged is \"error getting tenant from HTTP\" with the underlying reason in the err field.","triggerScenarios":"Raised when tenancy.GetTenantFromHTTP(r, h.options.TenantHeader, h.options.DefaultTenantID, h.options.TenantField) returns an error — typically because the configured tenant header is missing from the request, or the request's tenant value is empty/invalid given the handler's tenancy options.","commonSituations":"OTLP SDK/collector not configured to send the X-Scope-OrgID (or custom --receive.tenant-header) header; proxy or ingress strips the custom tenant header; client sends the tenant in a different header than the server expects; default tenant ID misconfigured.","solutions":["Configure the OTLP exporter/collector to send the expected tenant header (e.g. X-Scope-OrgID).","Align the client's tenant header name with the server's --receive.tenant-header option.","Check ingress/proxy configurations (e.g. nginx, Envoy) for header stripping rules and allow the tenant header through.","If anonymous writes are acceptable, set a non-empty default tenant via the receive default-tenant flag so extraction never fails."],"exampleFix":"// before: OTLP exporter without tenant header\ngrpcHeaders: {}\n// after\ngRPCMetadata:\n  - key: \"x-scope-orgid\"\n    value: \"my-tenant\"","handlingStrategy":"validation","validationCode":"// client side: ensure the tenant header is present before sending OTLP\nconst tenantHeader = \"X-Scope-OrgID\" // must match --receive.tenant-header\nif req.Header.Get(tenantHeader) == \"\" {\n\treq.Header.Set(tenantHeader, tenant)\n}","typeGuard":null,"tryCatchPattern":"// Go: handle 400 from the receiver\nresp, err := http.DefaultClient.Do(req)\nif err == nil && resp.StatusCode == http.StatusBadRequest {\n\tbody, _ := io.ReadAll(resp.Body)\n\treturn fmt.Errorf(\"tenant rejected by receiver (status 400): %s\", body)\n}","preventionTips":["Set the tenant header explicitly in every OTLP exporter/collector config.","Keep client and server tenant header names in one shared config source.","Audit ingress/proxy header allowlists when adding custom tenant headers.","In tests, assert the tenant header survives the full proxy chain."],"tags":["http","tenancy","otlp","multi-tenancy","bad-request"],"backgroundTag":"missing-required-header","analyzedSha":"35b8b991177def87ed52dcf10f9b6d87f07282c8","analyzedAt":"2026-09-07T01:49:59.689Z","contentChangedAt":"2026-09-07T01:49:59.689Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}