chinabugotech/hutool · error · IORuntimeException

Can not read file path of [{}]

Error message

Can not read file path of [{}]

What it means

When listFileNames resolves a path containing the JAR path extension marker (e.g. "/path/to/x.jar!/com/foo"), it opens it as a JarFile and lists the inner entries. If opening or reading the JAR throws IOException, it is wrapped and rethrown as IORuntimeException("Can not read file path of [{}]").

Source

Thrown at hutool-core/src/main/java/cn/hutool/core/io/FileUtil.java:265

			final List<String> paths = new ArrayList<>();
			final File[] files = ls(path);
			for (File file : files) {
				if (file.isFile()) {
					paths.add(file.getName());
				}
			}
			return paths;
		}

		// jar文件中的路径
		index = index + FileUtil.JAR_FILE_EXT.length();
		JarFile jarFile = null;
		try {
			jarFile = new JarFile(path.substring(0, index));
			// 防止出现jar!/cn/hutool/这类路径导致文件找不到
			return ZipUtil.listFileNames(jarFile, StrUtil.removePrefix(path.substring(index + 1), "/"));
		} catch (IOException e) {
			throw new IORuntimeException(StrUtil.format("Can not read file path of [{}]", path), e);
		} finally {
			IoUtil.close(jarFile);
		}
	}

	/**
	 * 创建File对象,相当于调用new File(),不做任何处理
	 *
	 * @param path 文件路径,相对路径表示相对项目路径
	 * @return File
	 * @since 4.1.4
	 */
	public static File newFile(String path) {
		return new File(path);
	}

	/**
	 * 创建File对象,自动识别相对或绝对路径,相对路径将自动从ClassPath下寻找

View on GitHub (pinned to 8870454b2a)

Solutions

  1. Verify the JAR portion of the path exists and is a readable file before listing.
  2. Confirm the path genuinely uses the ".jar!/entry" convention and is not a false match.
  3. Catch IORuntimeException, inspect getCause() for the underlying IOException, and report the specific JAR path.

Example fix

// before
List<String> names = FileUtil.listFileNames("/app/lib/bad.jar!/cn/hutool");

// after: confirm the jar exists first
String jarPart = "/app/lib/bad.jar";
if (FileUtil.isFile(jarPart)) {
    List<String> names = FileUtil.listFileNames(jarPart + "!/cn/hutool");
}
Defensive patterns

Strategy: try-catch

Validate before calling

String jarPart = path.substring(0, path.lastIndexOf(".jar!") + 4);
if (!FileUtil.isFile(jarPart)) {
    // JAR missing/unreadable: do not attempt to list
}

Type guard

static boolean isReadableJarPath(String path) {
    int i = path.lastIndexOf(".jar!");
    if (i < 0) return false;
    File jar = new File(path.substring(0, i + 4));
    return jar.isFile() && jar.canRead();
}

Try / catch

try {
    return FileUtil.listFileNames(jarPath);
} catch (IORuntimeException e) {
    Throwable cause = e.getCause(); // underlying IOException
    // report which JAR could not be read; treat as missing resource
}

Prevention

When it happens

Trigger: Calling a file-name listing method on a path with a ".jar!" marker where the JAR is missing, corrupt, truncated, locked, or the inner entry path is malformed.

Common situations: Corrupt or partially-downloaded JAR; JAR deleted between path resolution and read; a path that coincidentally contains ".jar!" but is not a real JAR; permission/lock preventing the JarFile constructor from opening it.

Related errors


AI-assisted analysis of chinabugotech/hutool@8870454b2a (2026-08-14). Data as JSON: /api/errors/75c1f89c5f165add. Report an issue: GitHub.