Resolving Stack Overflow and Infinite Recursion in Prolog
Learn how to diagnose and fix stack overflow errors in Prolog caused by left-recursion using tracing, clause reordering, and right-recursive transformations.
27 Sept 2026, 23:49 UTC

The Problem: Non-Terminating Queries
In Prolog, a program that appears logically correct may fail during execution by entering an infinite loop or crashing with a stack overflow error. This typically happens when the Prolog engine, which uses a depth-first search strategy, encounters a rule that calls itself before it can satisfy any other condition. This is known as left-recursion.
The immediate takeaway is that Prolog evaluates goals from left to right. If the first goal of a rule is the rule itself, the engine will recurse indefinitely without ever reaching a base case that could stop the process.
Diagnostic Matrix
Use this table to identify the specific behavior of your failing predicate.
| Symptom | Probable Cause | Diagnostic Indicator |
|---|---|---|
Instant stack overflow |
Direct Left-Recursion | The predicate head is the first goal in the body. |
| Hanging/Infinite Loop | Indirect Left-Recursion | Predicate A calls B, and B calls A without reducing the problem size. |
| Correct first result, then crash | Incorrect Clause Order | Recursive rule is placed before the base case. |
Step-by-Step Diagnostic Process
1. Static Code Analysis
Examine the predicate definition. Look for any clause where the predicate name in the head matches the first goal in the body.
% Example of a left-recursive rule
contains(X, List) :- contains(X, List), member(X, List).
In the example above, contains/2 calls itself immediately. The engine will never reach the member/2 check because it is stuck in a loop of calling contains/2.
2. Execution Tracing
Run the built-in trace utility to observe the goal resolution in real-time. This requires running the command in the Prolog top-level interpreter.
- Run
trace.to enable the debugger. - Execute your query (e.g.,
?- contains(a, [a,b,c]).). - Observe the call stack. If you see the same predicate being called repeatedly with the same arguments without any
ExitorFailmessages, you have confirmed a recursive loop. - Run
nodebug.to disable tracing.
Fixing the Recursion
Fix A: Reordering Clauses
Prolog attempts to satisfy clauses in the order they are written. If your recursive rule comes before your base case (the fact that stops the recursion), the program may recurse forever even if a solution exists.
Incorrect Order:
% This may loop if the recursive rule matches first
sum_list([H|T], Sum) :- sum_list(T, Rest), Sum is Rest + H.
sum_list([], 0).
Correct Order:
% Base case first ensures termination when the list is empty
sum_list([], 0).
sum_list([H|T], Sum) :- sum_list(T, Rest), Sum is Rest + H.
Fix B: Transforming to Right-Recursion
If the logic requires a recursive call, ensure it is not the first goal. Move the recursive call to the end of the body. This allows the engine to perform necessary checks or reductions before recursing.
Left-Recursive (Fails):
is_palindrome(List) :- is_palindrome(Tail), check_edges(List).
Right-Recursive (Succeeds):
is_palindrome([H|T]) :- check_edges([H|T]), is_palindrome(Tail).
Implementation Comparison
When choosing a fix, consider the impact on memory and logic:
| Method | Memory Impact | Logic Risk |
|---|---|---|
| Clause Reordering | Minimal | May change the order of solutions returned. |
| Right-Recursion | Increases stack usage | Generally safe, but avoids Tail Call Optimization (TCO). |
| Accumulators | Low (TCO enabled) | Requires a helper predicate (e.g., sum_list/3). |
Verification and Limitations
To verify the fix, execute the query again with trace enabled. You should see a sequence of Call, Exit, and Redo messages that eventually terminate. If the stack depth continues to grow linearly without any Exit calls, the recursion is still unresolved.
Limitations: These fixes assume you are using a standard Prolog engine (like SWI-Prolog) using depth-first search. Tabling (available in some modern Prologs) can resolve some left-recursion automatically by memoizing results, but it is not a universal substitute for clean rule design.
Rollback
Since these changes only modify the source code, rollback consists of reverting the .pl file to the previous git commit or backup version.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.