{"record":{"id":"ee04411a9512f097","repo":"spring-projects/spring-security","slug":"could-not-coerce-source-into-a-uri-string","errorCode":null,"errorMessage":"Could not coerce + source + into a URI String","messagePattern":"Could not coerce \\+ source \\+ into a URI String","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/MappedJwtClaimSetConverter.java","lineNumber":145,"sourceCode":"\t\tInstant result = (Instant) CONVERSION_SERVICE.convert(source, OBJECT_TYPE_DESCRIPTOR, INSTANT_TYPE_DESCRIPTOR);\n\t\tAssert.state(result != null, () -> \"Could not coerce \" + source + \" into an Instant\");\n\t\treturn result;\n\t}\n\n\tprivate static @Nullable String convertIssuer(Object source) {\n\t\tif (source == null) {\n\t\t\treturn null;\n\t\t}\n\t\tURL result = (URL) CONVERSION_SERVICE.convert(source, OBJECT_TYPE_DESCRIPTOR, URL_TYPE_DESCRIPTOR);\n\t\tif (result != null) {\n\t\t\treturn result.toExternalForm();\n\t\t}\n\t\tif (source instanceof String && ((String) source).contains(\":\")) {\n\t\t\ttry {\n\t\t\t\treturn new URI((String) source).toString();\n\t\t\t}\n\t\t\tcatch (Exception ex) {\n\t\t\t\tthrow new IllegalStateException(\"Could not coerce \" + source + \" into a URI String\", ex);\n\t\t\t}\n\t\t}\n\t\treturn (String) CONVERSION_SERVICE.convert(source, OBJECT_TYPE_DESCRIPTOR, STRING_TYPE_DESCRIPTOR);\n\t}\n\n\t@Override\n\tpublic Map<String, Object> convert(Map<String, Object> claims) {\n\t\tAssert.notNull(claims, \"claims cannot be null\");\n\t\tMap<String, Object> mappedClaims = new HashMap<>(claims);\n\t\tfor (Map.Entry<String, Converter<Object, ? extends @Nullable Object>> entry : this.claimTypeConverters\n\t\t\t.entrySet()) {\n\t\t\tString claimName = entry.getKey();\n\t\t\tConverter<Object, ? extends @Nullable Object> converter = entry.getValue();\n\t\t\tObject claim = claims.get(claimName);\n\t\t\t@SuppressWarnings(\"NullAway\")\n\t\t\tObject mappedClaim = converter.convert(claim);\n\t\t\tmappedClaims.compute(claimName, (key, value) -> mappedClaim);\n\t\t}","sourceCodeStart":127,"sourceCodeEnd":163,"githubUrl":"https://github.com/spring-projects/spring-security/blob/96852e8860138a482cb13d1479573f24ff6443c6/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/MappedJwtClaimSetConverter.java#L127-L163","documentation":"MappedJwtClaimSetConverter's issuer conversion found an `iss` claim that looks like a URI (contains ':') but cannot be parsed by java.net.URI, so it throws this IllegalStateException. The converter normalizes the issuer claim to a URI string; an unparseable value means the token's issuer claim is malformed.","triggerScenarios":"MappedJwtClaimSetConverter.convert invoked on a Jwt whose `iss` claim is a String containing ':' (e.g. 'http://:bad url', values with illegal characters/spaces) and new URI((String) source) throws URISyntaxException.","commonSituations":"Authorization server misconfigured with an issuer containing spaces or invalid characters; claims smuggled/overwritten by custom token enhancers; tests constructing Jwt with arbitrary iss strings like 'test: value'.","solutions":["Fix the authorization server's issuer setting to a valid absolute URI (RFC 3986), e.g. https://issuer.example.com.","If the token comes from a third party you can't fix, replace the default converter: MappedJwtClaimSetConverter.withDefaults(Map.of(\"iss\", claim -> desiredValue)) to normalize or drop the claim.","Sanitize/validate issuer claims at token creation time so malformed values never reach the decoder."],"exampleFix":"// before\nJwt.withTokenValue(token).claim(\"iss\", \"my issuer: v1\") // invalid URI\n// after\nJwt.withTokenValue(token).claim(\"iss\", \"https://my-issuer.example.com\")\n// or override conversion:\n// MappedJwtClaimSetConverter.withDefaults(Map.of(\"iss\", claims -> \"https://my-issuer.example.com\"))","handlingStrategy":"validation","validationCode":"Object iss = jwt.getClaims().get(\"iss\");\nif (iss instanceof String s && s.contains(\":\")) {\n    try { new URI(s); } catch (URISyntaxException e) { /* reject/normalize before decoding */ }\n}","typeGuard":"boolean isValidIssuerUri(Object iss) {\n    if (!(iss instanceof String s) || !s.contains(\":\")) return false;\n    try { new URI(s); return true; } catch (URISyntaxException e) { return false; }\n}","tryCatchPattern":"try { jwtDecoder.decode(token); }\ncatch (IllegalStateException e) {\n    if (e.getMessage().startsWith(\"Could not coerce\")) { /* reject token: malformed iss claim */ }\n}","preventionTips":["Configure the auth server issuer as a strict RFC 3986 absolute URI","Validate iss values at token-issuance time with new URI(iss)","Override the iss converter via MappedJwtClaimSetConverter.withDefaults if you must tolerate non-URI issuers"],"tags":["jwt","claims","uri","conversion"],"backgroundTag":"invalid-url-format","analyzedSha":"96852e8860138a482cb13d1479573f24ff6443c6","analyzedAt":"2026-09-10T23:25:23.477Z","contentChangedAt":"2026-09-10T23:25:23.477Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}