{"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/956246301e03d757539f7b76dd5a91f298637563/junit-jupiter-params/src/main/java/org/junit/jupiter/params/converter/TypedArgumentConverter.java#L61-L97","documentation":"Thrown by TypedArgumentConverter (as ArgumentConversionException) during argument injection when the actual parameter type is not assignable from the converter's declared targetType. The converter was built to produce one type but the @ParameterizedTest parameter declares an incompatible type.","triggerScenarios":"A TypedArgumentConverter subclass (registered via @ConvertWith, or internally by @JavaTimeConversionPattern etc.) is applied to a parameter whose declared type differs from the targetType passed into the super(sourceType, targetType) constructor. E.g. a converter declared for Integer applied to a `long`/`Long` parameter, or a String->Instant converter applied to a LocalDate parameter.","commonSituations":"Copy-pasting a converter between parameterized tests with different parameter types; changing a parameter type without updating the converter; wrapper vs primitive mismatch; generic converter reused across targets.","solutions":["Align the @ParameterizedTest parameter type with the converter's declared targetType (or vice versa).","If the converter must satisfy several targets, make targetType a common supertype or switch to a plain ArgumentConverter instead of TypedArgumentConverter.","Double-check the super(sourceType, targetType) call in the converter constructor matches the real parameter type."],"exampleFix":"// before\n@ParameterizedTest\n@CsvSource({ \"42\" })\nvoid test(@ConvertWith(ToIntConverter.class) Long n) { }\n// ToIntConverter calls super(String.class, Integer.class)\n\n// after\n@ParameterizedTest\n@CsvSource({ \"42\" })\nvoid test(@ConvertWith(ToIntConverter.class) Integer n) { }","handlingStrategy":"validation","validationCode":"// Before registering a TypedArgumentConverter, assert type compatibility.\nClass<?> declaredTarget = Integer.class;            // what the converter was built for\nClass<?> parameterType = method.getParameterTypes()[i];\nif (!declaredTarget.isAssignableFrom(parameterType)\n        && !parameterType.isAssignableFrom(declaredTarget)) {\n    throw new IllegalStateException(\n        \"Converter target \" + declaredTarget + \" is incompatible with parameter \" + parameterType);\n}","typeGuard":"// Narrow to a converter known to match the parameter type.\nstatic <S, T> TypedArgumentConverter<S, T> forType(\n        Class<S> source, Class<T> target, Class<?> parameterType) {\n    if (!target.isAssignableFrom(parameterType)\n            && !parameterType.isAssignableFrom(target)) {\n        throw new IllegalArgumentException(\"type mismatch: \" + target + \" vs \" + parameterType);\n    }\n    return new TypedArgumentConverter<>(source, target) {\n        @Override protected T convert(S s) { return null; }\n    };\n}","tryCatchPattern":"try {\n    // invoke the parameterized test / conversion\n} catch (ArgumentConversionException e) {\n    if (e.getMessage().contains(\"cannot convert to type\")) {\n        // log and align the parameter type with the converter's target type\n    } else throw e;\n}","preventionTips":["Keep the converter's targetType and the parameter type next to each other in code reviews.","Add a unit test that constructs the converter and asserts convert() works for the exact parameter type.","Prefer SimpleArgumentConverter or a custom ArgumentConverter when one converter must serve several target types."],"tags":["junit","jupiter-params","argument-converter","parameterized-test","type-mismatch"],"analyzedSha":"956246301e03d757539f7b76dd5a91f298637563","analyzedAt":"2026-08-04T19:25:17.049Z","schemaVersion":2}