{"record":{"id":"cef2814e4d05e7bb","repo":"junit-team/junit5","slug":"s-cannot-convert-to-type-s-only-target-type","errorCode":null,"errorMessage":"%s cannot convert to type [%s]. Only target type [%s] is supported.","messagePattern":"(.+?) cannot convert to type \\[(.+?)\\]\\. Only target type \\[(.+?)\\] is supported\\.","errorType":"exception","errorClass":"ArgumentConversionException","httpStatus":null,"severity":"error","filePath":"junit-jupiter-params/src/main/java/org/junit/jupiter/params/converter/TypedArgumentConverter.java","lineNumber":79,"sourceCode":"\tpublic final @Nullable Object convert(@Nullable Object source, FieldContext context)\n\t\t\tthrows ArgumentConversionException {\n\n\t\treturn convert(source, context.getField().getType());\n\t}\n\n\tprivate T convert(@Nullable Object source, Class<?> actualTargetType) {\n\t\tif (source == null) {\n\t\t\treturn convert(null);\n\t\t}\n\t\tif (!this.sourceType.isInstance(source)) {\n\t\t\tString message = \"%s cannot convert objects of type [%s]. Only source objects of type [%s] are supported.\".formatted(\n\t\t\t\tgetClass().getSimpleName(), source.getClass().getTypeName(), this.sourceType.getTypeName());\n\t\t\tthrow new ArgumentConversionException(message);\n\t\t}\n\t\tif (!ReflectionUtils.isAssignableTo(this.targetType, actualTargetType)) {\n\t\t\tString message = \"%s cannot convert to type [%s]. Only target type [%s] is supported.\".formatted(\n\t\t\t\tgetClass().getSimpleName(), actualTargetType.getTypeName(), this.targetType.getTypeName());\n\t\t\tthrow new ArgumentConversionException(message);\n\t\t}\n\t\treturn convert(this.sourceType.cast(source));\n\t}\n\n\t/**\n\t * Convert the supplied {@code source} object of type {@code S} into an object\n\t * of type {@code T}.\n\t *\n\t * @param source the source object to convert; may be {@code null}\n\t * @return the converted object; may be {@code null} but only if the target\n\t * type is a reference type\n\t * @throws ArgumentConversionException if an error occurs during the\n\t * conversion\n\t */\n\tprotected abstract T convert(@Nullable S source) throws ArgumentConversionException;\n\n}\n","sourceCodeStart":61,"sourceCodeEnd":97,"githubUrl":"https://github.com/junit-team/junit5/blob/f070c699a08b5d8393df9afd147af5c5e90bb21b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/converter/TypedArgumentConverter.java#L61-L97","documentation":"Thrown by TypedArgumentConverter when the declared targetType (T) is not assignable to the actual target type required at the injection site (context.getField().getType()). The converter is bound to a specific output type; if the field/parameter expects a wider or different type, conversion cannot proceed safely. The message prints actualTargetType and targetType.","triggerScenarios":"A TypedArgumentConverter<String, LocalDate> is registered on a parameter or field whose declared type is LocalDateTime (or Object that the framework resolves to an incompatible concrete type). The `!ReflectionUtils.isAssignableTo(this.targetType, actualTargetType)` guard fires.","commonSituations":"Registering a converter that produces a subtype on a parameter declared as a sibling type. Refactoring the converter's targetType generic without updating the parameter signature. Mismatch between @ParameterizedTest parameter type and the converter's output generic.","solutions":["Make the parameter/field type match (or be a supertype of) the converter's declared targetType.","Update the converter's targetType generic to the type actually required by the parameter.","Ensure ReflectionUtils.isAssignableTo(converter.targetType, parameterType) holds for the pair."],"exampleFix":"// before\nclass ToLocalDate extends TypedArgumentConverter<String, LocalDate> { ... }\nvoid test(@ConvertWith(ToLocalDate.class) LocalDateTime v) { }\n\n// after\nclass ToLocalDateTime extends TypedArgumentConverter<String, LocalDateTime> { ... }\nvoid test(@ConvertWith(ToLocalDateTime.class) LocalDateTime v) { }","handlingStrategy":"validation","validationCode":"// Ensure converter target type is assignable to the parameter type\nClass<?> targetType = LocalDateTime.class; // converter's declared T\nClass<?> paramType = fieldOrParameter.getType();\nif (!targetType.isAssignableFrom(paramType) && !paramType.isAssignableFrom(targetType)) {\n    throw new IllegalStateException(\"Converter target \" + targetType + \" incompatible with \" + paramType);\n}","typeGuard":"static <S, T> boolean targetCompatible(TypedArgumentConverter<S, T> c, Class<?> actualTarget) {\n    return org.junit.platform.commons.util.ReflectionUtils.isAssignableTo(\n        c.getTargetType(), actualTarget);\n}","tryCatchPattern":null,"preventionTips":["Keep the converter's targetType generic equal to the parameter's declared type.","When refactoring a converter's generics, update every usage site.","Prefer widening the parameter type only to a true supertype of the converter's output."],"tags":["junit-jupiter","argument-conversion","typed-converter","type-mismatch"],"backgroundTag":null,"analyzedSha":"f070c699a08b5d8393df9afd147af5c5e90bb21b","analyzedAt":"2026-08-11T20:31:00.530Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}