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
- 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)
- Mark the recursive helper as not-inlinable or move it out of the snippet
- 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
- Never make a snippet transitively lower to itself; hoist recursion into non-snippet code
- Read the 'Complete stack trace of inlined methods' section to spot the cycle instantly
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
- too many arguments
- Invalid directed inlining rules file path: {path}
- Could not read directed inlining rules file: {path}
- {file}:{line}: {cause}
- Directed inlining rule must have non-empty caller and callee
AI-assisted analysis of oracle/graal@a66e9ccd1d (2026-08-14).
Data as JSON: /api/errors/716b77de5c275312.
Report an issue: GitHub.