{"record":{"id":"636b4283ab137d85","repo":"junit-team/junit5","slug":"expected-file-name-with-file-extension-but-got","errorCode":null,"errorMessage":"Expected file name with file extension, but got: ${fileName}","messagePattern":"Expected file name with file extension, but got: (.+?)","errorType":"exception","errorClass":"JUnitException","httpStatus":null,"severity":"error","filePath":"junit-platform-commons/src/main/java/org/junit/platform/commons/util/SearchPathUtils.java","lineNumber":72,"sourceCode":"\t\tvar simpleClassName = determineSimpleClassName(path);\n\t\tvar parent = path.getParent();\n\t\treturn parent == null ? simpleClassName : joinPathNamesWithPackageSeparator(parent.resolve(simpleClassName));\n\t}\n\n\tprivate static String joinPathNamesWithPackageSeparator(Path path) {\n\t\treturn IntStream.range(0, path.getNameCount()) //\n\t\t\t\t.mapToObj(i -> path.getName(i).toString()) //\n\t\t\t\t.collect(joining(PACKAGE_SEPARATOR_STRING));\n\t}\n\n\tstatic String determineSimpleClassName(Path file) {\n\t\treturn removeExtension(file.getFileName().toString());\n\t}\n\n\tprivate static String removeExtension(String fileName) {\n\t\tint lastDot = fileName.lastIndexOf(FILE_NAME_EXTENSION_SEPARATOR_CHAR);\n\t\tif (lastDot < 0) {\n\t\t\tthrow new JUnitException(\"Expected file name with file extension, but got: \" + fileName);\n\t\t}\n\t\treturn fileName.substring(0, lastDot);\n\t}\n\n\tprivate static boolean isClassOrSourceFile(String name) {\n\t\treturn name.endsWith(CLASS_FILE_SUFFIX) || (SOURCE_MODE && name.endsWith(SOURCE_FILE_SUFFIX));\n\t}\n\n\tprivate static boolean isClassFile(Path file) {\n\t\treturn file.getFileName().toString().endsWith(CLASS_FILE_SUFFIX);\n\t}\n\n\tprivate SearchPathUtils() {\n\t}\n}\n","sourceCodeStart":54,"sourceCodeEnd":88,"githubUrl":"https://github.com/junit-team/junit5/blob/f070c699a08b5d8393df9afd147af5c5e90bb21b/junit-platform-commons/src/main/java/org/junit/platform/commons/util/SearchPathUtils.java#L54-L88","documentation":"JUnitException thrown by SearchPathUtils.removeExtension when a file name contains no '.' (no extension). removeExtension is called from determineSimpleClassName and isModuleInfoOrPackageInfo during module classpath scanning. The upstream filter only passes .class/.java files (which always have extensions), so reaching this guard means an extension-less name slipped through filtering - a defensive failure.","triggerScenarios":"A path whose final segment has no '.' reaches SearchPathUtils.removeExtension - e.g. via custom calling code invoking SearchPathUtils internals directly, or an upstream filter bug that lets an extension-less file through to class-name derivation.","commonSituations":"Rare; would indicate either a custom caller of SearchPathUtils internals on an arbitrary path, a malformed module entry, or a bug in upstream filtering. Standard scanning pre-filters to .class/.java which always have extensions.","solutions":["Ensure the path is a .class/.java file before invoking SearchPathUtils.determineSimpleClassName","If reached through standard scanning, report a bug to JUnit with the offending classpath/module entry","Remove non-class files from module or jar entries that should not be scanned"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"if (fileName.indexOf('.') < 0) {\n    // skip - not a class/source file; do not call removeExtension\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Keep module/jar entries to .class/.java files only","Do not invoke SearchPathUtils internals on arbitrary paths","Clean build outputs of stray extension-less files"],"tags":["classpath-scanning","filesystem","defensive"],"backgroundTag":null,"analyzedSha":"f070c699a08b5d8393df9afd147af5c5e90bb21b","analyzedAt":"2026-08-11T20:31:00.530Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}