junit-team/junit5 · error · JUnitException

Failed to publish path

Error message

Failed to publish path

What it means

Thrown by AbstractExtensionContext.publishFileEntry() when the ThrowingConsumer<Path> action passed to ExtensionContext.publishFile() or publishDirectory() throws while writing to the resolved path. The engine catches the Throwable, rethrows unrecoverable exceptions, and wraps the rest in a JUnitException so the failure is attributable to the file-publishing step rather than the user's arbitrary exception type.

Source

Thrown at junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/AbstractExtensionContext.java:185

		};
		publishFileEntry(name, enhancedAction, path -> {
			Preconditions.condition(Files.isDirectory(path), () -> "Published path must be a directory: " + path);
			return FileEntry.from(path, null);
		});
	}

	private void publishFileEntry(String name, ThrowingConsumer<Path> action,
			Function<Path, FileEntry> fileEntryCreator) {
		Path dir = createOutputDirectory();
		Path path = dir.resolve(name);
		Preconditions.condition(path.getParent() != null && path.getParent().equals(dir),
			() -> "name must not contain path separators: " + name);
		try {
			action.accept(path);
		}
		catch (Throwable t) {
			UnrecoverableExceptions.rethrowIfUnrecoverable(t);
			throw new JUnitException("Failed to publish path", t);
		}
		Preconditions.condition(Files.exists(path), () -> "Published path must exist: " + path);
		FileEntry fileEntry = fileEntryCreator.apply(path);
		this.engineExecutionListener.fileEntryPublished(this.testDescriptor, fileEntry);
	}

	private Path createOutputDirectory() {
		try {
			return configuration.getOutputDirectoryCreator().createOutputDirectory(this.testDescriptor);
		}
		catch (IOException e) {
			throw new JUnitException("Failed to create output directory", e);
		}
	}

	@Override
	public Optional<ExtensionContext> getParent() {
		return Optional.ofNullable(this.parent);

View on GitHub (pinned to 956246301e)

Solutions

  1. Inspect the cause (getCause()) of the JUnitException — it is the original throwable from your action and pinpoints the failing I/O or logic step.
  2. Ensure your publishFile/publishDirectory action performs all writes to the supplied Path and closes all streams in a try-with-resources before returning.
  3. Verify the output directory is writable and has free space; the path is created by the configured OutputDirectoryCreator.
  4. Catch expected business exceptions inside your action and either recover or rethrow as a meaningful message, since the engine will wrap anything thrown.

Example fix

// before
context.publishFile("report.html", MediaType.TEXT_HTML, path -> {
    Files.writeString(path, buildHtml()); // buildHtml() may throw -> wrapped as 'Failed to publish path'
});
// after
context.publishFile("report.html", MediaType.TEXT_HTML, path -> {
    String html;
    try { html = buildHtml(); }
    catch (Exception e) { throw new IOException("Failed to build report HTML", e); }
    Files.writeString(path, html);
});
Defensive patterns

Strategy: try-catch

Validate before calling

// Validate writability before calling publishFile
Path outDir = Paths.get(System.getProperty("junit.jupiter.output.dir", "build/reports"));
if (!Files.isWritable(outDir)) {
    throw new IllegalStateException("Output dir not writable: " + outDir);
}

Try / catch

try {
    context.publishFile("report.html", MediaType.TEXT_HTML, this::writeReport);
} catch (JUnitException e) {
    Throwable cause = e.getCause();
    log.error("publishFile failed: {}", cause.getMessage());
    // fall back to in-test reporting or rethrow
}

Prevention

When it happens

Trigger: Calling ExtensionContext.publishFile(name, mediaType, action) or publishDirectory(name, action) where the action lambda throws an IOException, RuntimeException, or Error while writing to the supplied Path. Also fires if the action runs but a later Files.exists check inside the action's own writes fails due to the action rethrowing.

Common situations: Extensions that publish reports/screenshots/artifacts via publishFile and write to a path on a full or read-only disk, an action that opens a stream and forgets to handle a broken pipe, or an action whose content-generation logic throws (e.g. serialization failure). Also when the configured output directory is on a network mount that drops mid-write.

Related errors


AI-assisted analysis of junit-team/junit5@956246301e (2026-08-04). Data as JSON: /data/errors/abe6e1a3211ec843.json. Report an issue: GitHub.