{"record":{"id":"e3760a93de3802ed","repo":"quarkusio/quarkus","slug":"error-decoding-multipart-attribute-wrapped-nullpo","errorCode":null,"errorMessage":"Error decoding multipart attribute (wrapped NullPointerException/IllegalArgumentException)","messagePattern":"Error decoding multipart attribute \\(wrapped NullPointerException/IllegalArgumentException\\)","errorType":"exception","errorClass":"ErrorDataDecoderException","httpStatus":null,"severity":"error","filePath":"independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/multipart/QuarkusMultipartResponseDecoder.java","lineNumber":794,"sourceCode":"                } else {\n                    for (int i = 1; i < contents.length; i++) {\n                        final String charsetHeader = HttpHeaderValues.CHARSET.toString();\n                        if (contents[i].regionMatches(true, 0, charsetHeader, 0, charsetHeader.length())) {\n                            String values = StringUtil.substringAfter(contents[i], '=');\n                            Attribute attribute;\n                            try {\n                                attribute = factory.createAttribute(response, charsetHeader, cleanString(values));\n                            } catch (NullPointerException | IllegalArgumentException e) {\n                                throw new ErrorDataDecoderException(e);\n                            }\n                            currentFieldAttributes.put(HttpHeaderValues.CHARSET, attribute);\n                        } else {\n                            Attribute attribute;\n                            try {\n                                attribute = factory.createAttribute(response,\n                                        cleanString(contents[0]), contents[i]);\n                            } catch (NullPointerException | IllegalArgumentException e) {\n                                throw new ErrorDataDecoderException(e);\n                            }\n                            currentFieldAttributes.put(attribute.getName(), attribute);\n                        }\n                    }\n                }\n            }\n        }\n        // Is it a FileUpload\n        Attribute filenameAttribute = currentFieldAttributes.get(HttpHeaderValues.FILENAME);\n        if (currentStatus == MultiPartStatus.DISPOSITION) {\n            if (filenameAttribute != null) {\n                // FileUpload\n                currentStatus = MultiPartStatus.FILEUPLOAD;\n                // do not change the buffer position\n                return decodeMultipart(MultiPartStatus.FILEUPLOAD);\n            } else {\n                // Field\n                currentStatus = MultiPartStatus.FIELD;","sourceCodeStart":776,"sourceCodeEnd":812,"githubUrl":"https://github.com/quarkusio/quarkus/blob/e1c734241f34c7919086ceb4c9262b4a58f6de44/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/multipart/QuarkusMultipartResponseDecoder.java#L776-L812","documentation":"This ErrorDataDecoderException wraps a NullPointerException or IllegalArgumentException thrown while creating a generic (non-charset) disposition attribute such as a custom name/value pair. The attribute factory rejected the cleaned name or value, so the decoder fails the multipart decode. It means some parameter in the Content-Disposition line could not be turned into a valid attribute.","triggerScenarios":"An arbitrary disposition parameter (contents[0] as name, contents[i] as value) yields a null/empty name after cleanString, or contains characters that factory.createAttribute rejects, throwing NPE/IAE.","commonSituations":"Servers emitting unusual or malformed disposition parameters (e.g. 'name=' with empty value, stray semicolons producing empty tokens); custom headers injected into Content-Disposition by middleware.","solutions":["Inspect the raw Content-Disposition line and fix the malformed parameter on the server.","Ensure every ';'-separated token has a non-empty name and value.","Remove non-standard parameters the decoder does not need.","If needed, pre-normalize the response or use a permissive AttributeFactory implementation."],"exampleFix":"// before (empty token/value)\nContent-Disposition: form-data; ; name=; value=\"x\"\n// after\nContent-Disposition: form-data; name=\"file\"; value=\"x\"","handlingStrategy":"validation","validationCode":"// Validate each disposition token before serialization\ntokens.forEach(t -> {\n    if (t.name().isBlank() || t.value() == null || t.value().isBlank()) {\n        throw new IllegalStateException(\"Empty disposition token: \" + t);\n    }\n});","typeGuard":"static boolean hasValidTokens(String[] contents) {\n    return contents != null && contents.length > 1\n        && contents[0] != null && !contents[0].isBlank();\n}","tryCatchPattern":"try {\n    decoder.decodeMultipart(status);\n} catch (ErrorDataDecoderException e) {\n    log.warn(\"Unparseable disposition attribute: {}\", e.getCause(), e);\n    throw new MalformedMultipartException(e);\n}","preventionTips":["Avoid injecting custom parameters into Content-Disposition.","Serialize parameters with a tested multipart library, never by string concatenation.","Reject empty ';'-separated tokens in the serializer.","Golden-file tests comparing emitted part headers byte-for-byte."],"tags":["multipart","decoding","content-disposition","resteasy-reactive"],"backgroundTag":"multipart-decode-failed","analyzedSha":"e1c734241f34c7919086ceb4c9262b4a58f6de44","analyzedAt":"2026-09-05T17:01:29.979Z","contentChangedAt":"2026-09-05T17:01:29.979Z","schemaVersion":2},"datasetVersion":"2026-09-12T22:17:10.623Z"}