quarkusio/quarkus · error · UncheckedIOException

Unable to set tmp java.home for FontConfig Quarkus AWT…

Error message

Unable to set tmp java.home for FontConfig Quarkus AWT usage in ${javaHome}

What it means

On native image, Quarkus AWT's substitution for sun.awt.FontConfiguration (Linux) creates a temporary java.home layout (lib/ and conf/fonts directories) that FontConfig requires. If creating these directories fails with IOException, an UncheckedIOException with this message is thrown, aborting font initialization.

Solutions

  1. Ensure the directory backing java.io.tmpdir (or the path reported) is writable by the process user
  2. Mount a writable tmpfs at /tmp in the container (e.g. docker run --tmpfs /tmp:rw)
  3. Check disk space and permissions; run the binary with a user that owns its directory
  4. If java.home was mis-resolved, verify how the native binary locates its temp dir and pass -Djava.io.tmpdir to a writable path

Example fix

// before (read-only rootfs)
docker run --read-only myapp-image
// after
docker run --read-only --tmpfs /tmp:rw,noexec,nosuid myapp-image
Defensive patterns

Strategy: validation

Validate before calling

Path tmp = Path.of(System.getProperty("java.io.tmpdir"));
if (!Files.isWritable(tmp)) {
    throw new IllegalStateException("Writable temp dir required for AWT/FontConfig in native mode: " + tmp);
}

Try / catch

try {
    BufferedImage img = new BufferedImage(1, 1, BufferedImage.TYPE_INT_RGB);
    Graphics2D g = img.createGraphics();
} catch (UncheckedIOException e) {
    if (e.getMessage() != null && e.getMessage().contains("Unable to set tmp java.home")) {
        log.error("Mount a writable /tmp or grant write access: " + e.getCause());
    }
    throw e;
}

Prevention

When it happens

Trigger: First AWT/Font usage in a native executable where the temp directory (usually io.tmpdir under the app dir) cannot be created: read-only filesystem, permission denied, or no space.

Common situations: Running the native binary in a read-only container (distroless, read-only /tmp); running as a non-root user without write access; security-hardened runtime blocking directory creation.

Related errors


AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05). Data as JSON: /api/errors/1d69c587d0c1c6a3. Report an issue: GitHub.

Appendix: source

Thrown at extensions/awt/runtime/src/main/java/io/quarkus/awt/runtime/JDKSubstitutions.java:69

 */
@TargetClass(className = "sun.awt.FontConfiguration", onlyWith = IsLinux.class)
final class Target_sun_awt_FontConfiguration_Linux {
    @Alias
    protected static String osVersion;
    @Alias
    protected static String osName;

    @Substitute
    protected void setOsNameAndVersion() {
        final Path javaHome = Path.of(System.getProperty("java.io.tmpdir"), "quarkus-awt-tmp-fonts");
        try {
            System.setProperty("java.home", javaHome.toString());
            osName = System.getProperty("os.name", "unknown");
            osVersion = System.getProperty("os.version");
            Files.createDirectories(javaHome.resolve("lib"));
            Files.createDirectories(javaHome.resolve("conf").resolve("fonts"));
        } catch (IOException e) {
            throw new UncheckedIOException("Unable to set tmp java.home for FontConfig Quarkus AWT usage in " + javaHome, e);
        }
    }
}

/**
 * See Target_sun_awt_FontConfiguration_Linux, for context.
 *
 * Windows doesn't have fontconfig package and its config file installed like Linux has.
 * Java runtime looks for the config file inside our fake JAVA_HOME.
 * We provide a skeleton, i18n ignorant version to satisfy the basic headless fonts processing.
 */
@TargetClass(className = "sun.awt.FontConfiguration", onlyWith = IsWindows.class)
final class Target_sun_awt_FontConfiguration_Windows {
    @Alias
    protected static String osVersion;
    @Alias
    protected static String osName;

View on GitHub (pinned to e1c734241f)