oracle/graal · error · PermanentBailoutException

Too deep inlining, probably caused by recursive inlining.

Error message

Too deep inlining, probably caused by recursive inlining.

What it means

While decoding snippet/intrinsic graphs, PEGraphDecoder tracks inlining depth. When inlining during decoding exceeds the allowed depth, it builds a diagnostic message listing the full chain of inlined methods (the code around PEGraphDecoder.java:1673) and throws PermanentBailoutException with 'Too deep inlining, probably caused by recursive inlining.' The named methods show the recursion cycle.

Source

Thrown at compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/replacements/PEGraphDecoder.java:1673

        List<Map.Entry<ResolvedJavaMethod, Integer>> methods = new ArrayList<>(methodCounts.entrySet());
        methods.sort((e1, e2) -> -Integer.compare(e1.getValue(), e2.getValue()));

        StringBuilder msg = new StringBuilder("Too deep inlining, probably caused by recursive inlining.").append(System.lineSeparator()).append("== Inlined methods ordered by inlining frequency:");
        for (Map.Entry<ResolvedJavaMethod, Integer> entry : methods) {
            msg.append(System.lineSeparator()).append(entry.getKey().format("%H.%n(%p) [")).append(entry.getValue()).append("]");
        }
        msg.append(System.lineSeparator()).append("== Complete stack trace of inlined methods:");
        int lastBci = 0;
        for (PEMethodScope cur = methodScope; cur != null; cur = cur.caller) {
            msg.append(System.lineSeparator()).append(cur.method.asStackTraceElement(lastBci));
            if (cur.invokeData != null) {
                lastBci = cur.invokeData.invoke.bci();
            } else {
                lastBci = 0;
            }
        }

        throw new PermanentBailoutException(msg.toString());
    }

    protected FixedNode nodeAfterInvoke(PEMethodScope methodScope, LoopScope loopScope, InvokeData invokeData, BeginNode prevBegin) {
        assert prevBegin == null || prevBegin.isAlive();
        if (invokeData.invoke instanceof InvokeWithExceptionNode) {
            if (prevBegin != null && getNodeClass(methodScope, loopScope, invokeData.nextOrderId) == prevBegin.getNodeClass()) {
                // Reuse the previous Node but mark it in nodesToProcess so that the decoding loop
                // continues decoding.
                loopScope.nodesToProcess.set(invokeData.nextOrderId);
                registerNode(loopScope, invokeData.nextOrderId, prevBegin, false, false);
                return prevBegin;
            }
        }
        return makeStubNode(methodScope, loopScope, invokeData.nextOrderId);
    }

    private static void deleteInvoke(Invoke invoke) {
        /*

View on GitHub (pinned to a66e9ccd1d)

Solutions

  1. Read the '== Complete stack trace of inlined methods ==' section in the message to find the cycle, then break the recursion (base-case check, non-snippet escape, or different snippet for the recursive step)
  2. Mark the recursive helper as not-inlinable or move it out of the snippet
  3. If this occurs without custom snippets, report a GraalVM bug with the stack section

Example fix

// before
@Snippet static int len(Object[] a, int i) { return i >= a.length ? 0 : 1 + len(a, i + 1); }

// after
static int len(Object[] a, int i) { ... } // non-snippet recursive helper
@Snippet static int lenSnippet(Object[] a) { return len(a, 0); }
Defensive patterns

Strategy: try-catch

Try / catch

try {
    graph = replacements.getSnippet(...);
} catch (PermanentBailoutException e) {
    // message contains the inlined-method cycle; fix the recursive snippet, then recompile
    log.error("snippet recursion: {}", e.getMessage());
}

Prevention

When it happens

Trigger: A snippet or intrinsic whose partial-evaluation graph recursively inlines itself (directly or through a cycle of snippets), each level increasing decode depth until the limit is hit.

Common situations: Writing a snippet that calls a method that is itself lowered to the same snippet; snippets with unguarded recursive helper calls; changing a method from regular to @Snippet so a pre-existing recursion now becomes snippet recursion.

Related errors


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