oracle/graal · error · IllegalArgumentException

LogFile substitution %s cannot be combined with any other ch

Error message

LogFile substitution %s cannot be combined with any other characters

What it means

GraalVM throws IllegalArgumentException from HotSpotTTYStreamProvider when the -Dgraal.LogFile value uses the %o (stdout) or %e (stderr) substitution combined with any other characters. These two substitutions must constitute the entire LogFile value because they redirect compiler log output wholesale to an existing stream rather than to a file path. Any prefix/suffix (e.g. 'graal_%o.log') makes the resulting name meaningless, so the provider rejects it up front.

Source

Thrown at compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/HotSpotTTYStreamProvider.java:142

         */
        private static String makeFilename(String nameTemplate) {
            String name = nameTemplate;
            if (name.contains("%p")) {
                name = name.replace("%p", GraalServices.getExecutionID());
            }
            if (name.contains("%i")) {
                name = name.replace("%i", IsolateUtil.getIsolateID(false));
            }
            if (name.contains("%I")) {
                name = name.replace("%I", IsolateUtil.getIsolateID(true));
            }
            if (name.contains("%t")) {
                name = name.replace("%t", String.valueOf(GraalServices.milliTimeStamp()));
            }

            for (String subst : new String[]{"%o", "%e"}) {
                if (name.contains(subst) && !name.equals(subst)) {
                    throw new IllegalArgumentException("LogFile substitution " + subst + " cannot be combined with any other characters");
                }
            }

            return name;
        }

        /**
         * An output stream that redirects to {@link HotSpotJVMCIRuntime#getLogStream()}. The
         * {@link HotSpotJVMCIRuntime#getLogStream()} value is only accessed the first time an IO
         * operation is performed on the stream. This is required to break a deadlock in early JVMCI
         * initialization.
         */
        class DelayedOutputStream extends OutputStream {
            private volatile OutputStream lazy;

            private OutputStream lazy() {
                if (lazy == null) {
                    synchronized (this) {

View on GitHub (pinned to a66e9ccd1d)

Solutions

  1. Use %o or %e alone: -Dgraal.LogFile=%o (or %e) with no other characters.
  2. If you need generated file names, use the embeddable substitutions instead: %i/%I (isolate id) or %t (timestamp), e.g. -Dgraal.LogFile='graal_%i.log'.
  3. If you need a literal percent-containing name, pick a name without %o/%e substrings.

Example fix

# before
-Dgraal.LogFile=graal_%o.log

# after (redirect to stdout wholesale)
-Dgraal.LogFile=%o
# or, generated file name
-Dgraal.LogFile=graal_%i.log
Defensive patterns

Strategy: validation

Validate before calling

// Java: validate the LogFile value before enabling compiler logging
String logFile = System.getProperty("graal.LogFile", "");
for (String wholeOnly : new String[]{"%o", "%e"}) {
    if (logFile.contains(wholeOnly) && !logFile.equals(wholeOnly)) {
        throw new IllegalArgumentException(
            "graal.LogFile: " + wholeOnly + " must be the entire value, got: " + logFile);
    }
}

Try / catch

try {
    HotSpotTTYStreamProvider.openLogStream(); // or launch JVM with graal.LogFile
} catch (IllegalArgumentException e) {
    if (e.getMessage().contains("cannot be combined")) { /* fix the property, retry */ }
    throw e;
}

Prevention

When it happens

Trigger: Setting -Dgraal.LogFile=%o or -Dgraal.LogFile=%e together with extra text, e.g. -Dgraal.BenchmarkCounters... plus -Dgraal.LogFile='log_%o'. Only the substitutions %i, %I and %t may be embedded in a larger file name; %o and %e are whole-value-only.

Common situations: A developer wants compiler logs tagged per isolate or timestamp but mistakenly uses %o/%e as a name fragment; or copy-pastes a LogFile pattern from a script that appends a suffix. Common in benchmarking setups where each run writes 'graal_<n>.log' and someone swaps in %o expecting stream capture.

Related errors


AI-assisted analysis of oracle/graal@a66e9ccd1d (2026-08-14). Data as JSON: /api/errors/6acb9a53c190094b. Report an issue: GitHub.