{"record":{"id":"65287acb9ec48582","repo":"spring-projects/spring-security","slug":"an-error-occurred-writing-the-token-introspection","errorCode":null,"errorMessage":"An error occurred writing the Token Introspection Response: + ex.getMessage()","messagePattern":"An error occurred writing the Token Introspection Response: \\+ ex\\.getMessage\\(\\)","errorType":"http","errorClass":"HttpMessageNotWritableException","httpStatus":500,"severity":"error","filePath":"oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/http/converter/OAuth2TokenIntrospectionHttpMessageConverter.java","lineNumber":107,"sourceCode":"\t\t\treturn this.tokenIntrospectionConverter.convert(tokenIntrospectionParameters);\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tthrow new HttpMessageNotReadableException(\n\t\t\t\t\t\"An error occurred reading the Token Introspection Response: \" + ex.getMessage(), ex, inputMessage);\n\t\t}\n\t}\n\n\t@Override\n\tprotected void writeInternal(OAuth2TokenIntrospection tokenIntrospection, HttpOutputMessage outputMessage)\n\t\t\tthrows HttpMessageNotWritableException {\n\t\ttry {\n\t\t\tMap<String, Object> tokenIntrospectionResponseParameters = this.tokenIntrospectionParametersConverter\n\t\t\t\t.convert(tokenIntrospection);\n\t\t\tthis.jsonMessageConverter.write(tokenIntrospectionResponseParameters, STRING_OBJECT_MAP.getType(),\n\t\t\t\t\tMediaType.APPLICATION_JSON, outputMessage);\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tthrow new HttpMessageNotWritableException(\n\t\t\t\t\t\"An error occurred writing the Token Introspection Response: \" + ex.getMessage(), ex);\n\t\t}\n\t}\n\n\t/**\n\t * Sets the {@link Converter} used for converting the Token Introspection Response\n\t * parameters to an {@link OAuth2TokenIntrospection}.\n\t * @param tokenIntrospectionConverter the {@link Converter} used for converting to an\n\t * {@link OAuth2TokenIntrospection}\n\t */\n\tpublic final void setTokenIntrospectionConverter(\n\t\t\tConverter<Map<String, Object>, OAuth2TokenIntrospection> tokenIntrospectionConverter) {\n\t\tAssert.notNull(tokenIntrospectionConverter, \"tokenIntrospectionConverter cannot be null\");\n\t\tthis.tokenIntrospectionConverter = tokenIntrospectionConverter;\n\t}\n\n\t/**\n\t * Sets the {@link Converter} used for converting an {@link OAuth2TokenIntrospection}","sourceCodeStart":89,"sourceCodeEnd":125,"githubUrl":"https://github.com/spring-projects/spring-security/blob/96852e8860138a482cb13d1479573f24ff6443c6/oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/http/converter/OAuth2TokenIntrospectionHttpMessageConverter.java#L89-L125","documentation":"OAuth2TokenIntrospectionHttpMessageConverter.writeInternal wraps any exception raised while converting OAuth2TokenIntrospection to its response parameter Map and writing it as JSON to the HTTP output stream. Failures in tokenIntrospectionParametersConverter.convert() or jsonMessageConverter.write() are rethrown as org.springframework.http.converter.HttpMessageNotWritableException with the cause preserved. It indicates the token introspection response could not be serialized and returned to the caller.","triggerScenarios":"Calling OAuth2TokenIntrospectionHttpMessageConverter.write() (via writeInternal) when tokenIntrospectionParametersConverter.convert() throws (invalid introspection state) or jsonMessageConverter.write() fails to serialize the Map<String,Object> to JSON (no JSON converter, broken output stream, unserializable custom claim).","commonSituations":"An introspection endpoint implementation adds a custom claim value the ObjectMapper cannot serialize; the client disconnected before the response was fully written (broken pipe); the JSON HttpMessageConverter was replaced/removed in configuration; a custom Converter set via setTokenIntrospectionParametersConverter throws on certain introspection instances.","solutions":["Inspect ex.getCause() to identify whether the failure is serialization, converter, or I/O related and fix that root cause.","Ensure a functioning JSON HttpMessageConverter (MappingJackson2HttpMessageConverter) is available or set via setJsonMessageConverter.","Confirm the OAuth2TokenIntrospection instance and any additional parameters contain only JSON-serializable values with correct types (e.g. boolean active).","Review custom code registered via setTokenIntrospectionParametersConverter for exceptions on your data.","Handle client-disconnect I/O errors as benign — log at debug and do not retry writing to a committed response."],"exampleFix":"// before\nintrospection.set(\"scope_grants\", someNonSerializableDomainObject);\n// after\nintrospection.set(\"scope_grants\", objectMapper.valueToTree(someDomainObject)); // serialize to JSON-compatible form first","handlingStrategy":"try-catch","validationCode":"// before writing\nassert tokenIntrospection.isActive() != null || tokenIntrospection.getParameters().isEmpty();\nobjectMapper.writeValueAsString(tokenIntrospection.getParameters()); // fail fast on unserializable claims","typeGuard":null,"tryCatchPattern":"try {\n  converter.write(tokenIntrospection, MediaType.APPLICATION_JSON, outputMessage);\n} catch (HttpMessageNotWritableException e) {\n  logger.error(\"Failed to write token introspection response\", e.getCause());\n}\n// do not rethrow to a committed servlet response; log and return","preventionTips":["Serialize custom claim values to Jackson-compatible types before adding them to introspection.","Keep a working MappingJackson2HttpMessageConverter in the converter chain.","Treat broken-pipe/client-disconnect write errors as debug-level noise.","Unit-test writing introspection responses including edge cases (inactive token, extra claims)."],"tags":["spring-security","oauth2","token-introspection","json-serialization"],"backgroundTag":"json-marshal-failed","analyzedSha":"96852e8860138a482cb13d1479573f24ff6443c6","analyzedAt":"2026-09-10T23:25:23.477Z","contentChangedAt":"2026-09-10T23:25:23.477Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}