{"record":{"id":"e85092fd071c3620","repo":"pinpoint-apm/pinpoint","slug":"otlp-http-trace-request-rejected-unsupported-cont","errorCode":null,"errorMessage":"OTLP/HTTP trace request rejected. Unsupported Content-Encoding={}","messagePattern":"OTLP/HTTP trace request rejected\\. Unsupported Content-Encoding=(.+?)","errorType":"http","errorClass":null,"httpStatus":415,"severity":"warning","filePath":"otlptrace/otlptrace-collector/src/main/java/com/navercorp/pinpoint/otlp/trace/collector/controller/OtlpTraceDecompressionFilter.java","lineNumber":93,"sourceCode":"\n    public OtlpTraceDecompressionFilter(int maxDecompressedBytes, OtlpTraceIngestMetrics ingestMetrics) {\n        this.maxDecompressedBytes = maxDecompressedBytes;\n        this.ingestMetrics = Objects.requireNonNull(ingestMetrics, \"ingestMetrics\");\n    }\n\n    @Override\n    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)\n            throws ServletException, IOException {\n\n        final String encoding = request.getHeader(HttpHeaders.CONTENT_ENCODING);\n        if (encoding == null || encoding.isBlank() || IDENTITY.equalsIgnoreCase(encoding.trim())) {\n            filterChain.doFilter(request, response);\n            return;\n        }\n        if (!GZIP.equalsIgnoreCase(encoding.trim())) {\n            // Only gzip is defined for OTLP/HTTP; reject anything else (incl. multi-encoding) explicitly\n            // rather than letting an undecoded body fail later as an opaque 400.\n            logger.warn(\"OTLP/HTTP trace request rejected. Unsupported Content-Encoding={}\", encoding);\n            ingestMetrics.requestRejected(OtlpTraceIngestMetrics.Transport.HTTP, OtlpTraceIngestMetrics.RequestRejectReason.UNSUPPORTED_ENCODING);\n            response.setStatus(HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE);\n            return;\n        }\n\n        filterChain.doFilter(new GzipRequestWrapper(request, maxDecompressedBytes), response);\n    }\n\n    /**\n     * Presents a gzip-encoded request as its decompressed body. Content-Length now describes the\n     * compressed size, so it is cleared (reported as unknown) to stop a downstream reader from\n     * truncating the inflated stream at the compressed length.\n     */\n    private static final class GzipRequestWrapper extends HttpServletRequestWrapper {\n        private final int limit;\n        // Created lazily and cached so repeated getInputStream() calls return the same instance\n        // (servlet wrapper contract). A second GZIPInputStream over the already-consumed underlying\n        // stream would otherwise fail to re-read the gzip header. Single-threaded per request.","sourceCodeStart":75,"sourceCodeEnd":111,"githubUrl":"https://github.com/pinpoint-apm/pinpoint/blob/744c3d3075e595656abb1ae331ad2c0e4c9eb996/otlptrace/otlptrace-collector/src/main/java/com/navercorp/pinpoint/otlp/trace/collector/controller/OtlpTraceDecompressionFilter.java#L75-L111","documentation":"OtlpTraceDecompressionFilter.doFilterInternal rejects OTLP/HTTP trace requests whose Content-Encoding is not 'gzip'. The OTLP/HTTP spec only defines gzip compression; anything else (identity mislabeled, deflate, br, or multi-encodings like 'gzip, br') is rejected up front with HTTP 415 UNSUPPORTED_MEDIA_TYPE so the caller gets an explicit error instead of an opaque 400 from a garbled undecoded protobuf body.","triggerScenarios":"Sending a POST to the OTLP/HTTP trace endpoint with a Content-Encoding header other than 'gzip' (e.g. 'deflate', 'zstd', 'br', 'gzip, deflate', or 'identity' while actually compressing).","commonSituations":"Client SDK configured with a non-default compression (zstd/deflate), an HTTP proxy or gateway adding its own Content-Encoding on top, or a client compressing with 'identity' set. Often surfaces after upgrading an SDK where compression defaulted to none before.","solutions":["Set the OTLP exporter's compression to gzip (OTEL_EXPORTER_OTLP_COMPRESSION=gzip or the SDK's compression option).","Remove any Content-Encoding header injection by intermediate proxies/load balancers.","Send the request uncompressed (no Content-Encoding) if compression is not needed — the filter passes it through.","If using a custom client, do not send multiple encodings; gzip alone is accepted."],"exampleFix":"// before\nOtlpHttpTraceExporter.builder()\n    .setCompression(\"deflate\")\n    .build();\n\n// after\nOtlpHttpTraceExporter.builder()\n    .setCompression(\"gzip\")\n    .build();","handlingStrategy":"validation","validationCode":"// client-side, before sending\nString enc = request.getHeader(\"Content-Encoding\");\nboolean ok = enc == null || \"gzip\".equalsIgnoreCase(enc.trim());\nif (!ok) {\n    throw new IllegalStateException(\"OTLP/HTTP allows only gzip Content-Encoding, got: \" + enc);\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Set OTEL_EXPORTER_OTLP_COMPRESSION=gzip explicitly rather than relying on defaults.","Never let an HTTP proxy add or rewrite Content-Encoding on OTLP endpoints.","Expect HTTP 415 from the collector when encoding is wrong; treat it as a client config bug, not transient."],"tags":["http","otlp","compression","content-encoding"],"backgroundTag":"http-error-response","analyzedSha":"744c3d3075e595656abb1ae331ad2c0e4c9eb996","analyzedAt":"2026-09-07T18:48:45.289Z","contentChangedAt":"2026-09-07T18:48:45.289Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}