Fix PARI timeout detection on Ubuntu 25.10
Fix PARI evaluation timeout detection on Ubuntu 25.10
Problem
The test-pari test suite fails when PARI/GP times out because the timeout detection doesn't recognize PARI's alarm interrupt messages. When PARI times out, it outputs error messages like:
*** _*_: alarm interrupt after 59,952 ms.
The code tries to parse these error messages as numbers, causing an exception.
Plan
- [x] Explore codebase and understand the issue
- [x] Build and verify fast tests pass
- [x] Modify
SequenceUtil::evalFormulaWithExternalToolto detect PARI error messages in output - [x] Test the fix with a timeout scenario
- [x] Run comprehensive edge case tests
- [x] Address review feedback to make error detection more specific
- [x] Run security checks
- [x] Verify all tests still pass
Solution
Modified seq_util.cpp to check for PARI error messages before attempting to parse output as numbers. The detection is now more specific:
- Only checks when
toolName == "PARI"to avoid masking errors from other tools - Looks for the specific pattern
" ***"(2 spaces + 3 asterisks) instead of the broader" *" - Returns
falseon error detection to signal timeout instead of throwing exception
Changes
- Initial implementation: Detect lines starting with
" *" - Refined based on review: Made pattern more specific (
" ***") and tool-specific (PARI only)
Testing
- Created and ran comprehensive edge case tests including:
- Normal numeric output for PARI and other tools
- PARI error messages with various formats
- Real PARI timeout messages from the issue
- Verification that non-PARI tools are not affected
All tests pass successfully.
Original prompt
This section details on the original issue you should resolve
<issue_title>test-pari fails on Ubuntu 25.10 due to unregognized timeout</issue_title>
<issue_description>On Ubuntu 25.10, the long-running test suite test-pari fails with the following error:
$ ./loda test-pari
2025-11-15 16:18:38|INFO |Starting LODA developer version. See https://loda-lang.org/
2025-11-15 16:18:38|INFO |Using LODA home directory "/home/me/loda/"
2025-11-15 16:18:42|INFO |Loaded 377622/390211 A-sequences in 3.74s
2025-11-15 16:18:44|INFO |Loaded stats for 146215 programs in 2.35s
2025-11-15 16:18:44|INFO |Checking 15 terms of A000002: (a(n) = if(n==2,2,if(n==1,1,if(n==0,0,gcd(a(n-1)+truncate((-c(n-1)+b(n-1))/2),2))))); (b(n) = if(n==2,-1,if(n==1,-1,if(n==0,0,truncate((-c(n-1)+b(n-1))/2))))); (c(n) = if(n==2,4,if(n==1,2,if(n==0,2,local(l1=c(n-1)); gcd(a(n-1)+truncate((-l1+b(n-1))/2),2)*l1))))
2025-11-15 16:18:45|INFO |Checking 102 terms of A000004: a(n) = 0
2025-11-15 16:18:45|INFO |Checking 105 terms of A000007: a(n) = n==0
2025-11-15 16:18:45|INFO |Checking 10 terms of A000008: a(n) = truncate((120*floor(n/5)*(-floor(((n%5)^2)/8)+floor(((-1)^(n%5)+1)/2))+15*n*(-1)^n+(n+17)*(20*n+2*n^2+81))/1200)
2025-11-15 16:18:45|INFO |Checking 90 terms of A000012: a(n) = 1
2025-11-15 16:18:45|INFO |Checking 15 terms of A000023: a(n) = if(n==3,-2,if(n==2,2,if(n==1,-1,if(n==0,1,local(l1=a(n-1)); (n-1)*(2*a(n-2)+l1)-l1))))
2025-11-15 16:18:45|INFO |Checking 77 terms of A000027: a(n) = n
...
...
...
2025-11-15 16:30:58|INFO |Checking 42 terms of A005491: a(n) = n^3+3*n+1
2025-11-15 16:30:58|INFO |Checking 37 terms of A005492: a(n) = (n-4)*((n-4)*((n-4)^2+6)+4)+4
2025-11-15 16:30:58|INFO |Checking 10 terms of A005508: a(n) = -2*binomial(n,floor((n+1)/2)+2)+binomial(n+1,floor((n+1)/2))
2025-11-15 16:30:58|INFO |Checking 30 terms of A005509: (a(n) = min(n,n%2)*c(n)+b(n)); (b(n) = if(n==5,26,if(n==4,26,if(n==3,7,if(n==2,7,if(n==1,1,if(n==0,1,local(l1=d(n-2)); truncate((b(n-2)*(l1*(-87*l1-2569)+2476)+c(n-2)*(l1*(29*l1+1011)+6884))/3304)))))))); (c(n) = if(n==5,42,if(n==4,42,if(n==3,11,if(n==2,11,if(n==1,3,if(n==0,3,local(l1=d(n-2)); truncate((b(n-2)*(l1*(l1*(39648*l1+344537)-2597)+620632)+c(n-2)*(l1*(-191939*l1-163769)+1307456))/(3304*l1^3+413000))))))))); (d(n) = if(n==5,2,if(n==4,2,if(n==3,1,if(n==2,1,if(n==1,0,if(n==0,0,d(n-2)+1)))))))
2025-11-15 16:30:58|INFO |Checking 15 terms of A005517: (a(n) = b(n-1)); (b(n) = if(n==6,25,if(n==5,15,if(n==4,9,if(n==3,5,if(n==2,3,if(n==1,2,if(n==0,1,5*b(n-3)))))))))
2025-11-15 16:30:59|INFO |Checking 15 terms of A005522: (a(n) = b(n)+c(n)+2); (b(n) = if(n==3,4,if(n==2,1,if(n==1,0,if(n==0,0,-d(n-1)+b(n-1)))))); (c(n) = if(n==4,7,if(n==3,4,if(n==2,3,if(n==1,1,if(n==0,2,c(n-1)+c(n-2))))))); (d(n) = if(n==3,-8,if(n==2,-3,if(n==1,-1,if(n==0,0,2*d(n-1)-b(n-1)-1)))))
2025-11-15 16:30:59|INFO |Checking 12 terms of A005527: a(n) = -floor(n/7)+min(n,1)+n+3
2025-11-15 16:30:59|INFO |Checking 5 terms of A005532: (a(n) = truncate(d(3*n+3)/truncate(c(3*n+3)/(10^(n-1))))%10); (b(n) = if(n==4,0,if(n==3,0,if(n==2,0,if(n==1,0,if(n==0,0,34*truncate((3*b(n-1)+2*c(n-1)+e(n-1)+f(n-1)-1)/68))))))); (c(n) = if(n==4,-10,if(n==3,-6,if(n==2,-3,if(n==1,-1,if(n==0,0,local(l2=c(n-1)); local(l1=f(n-1)); 34*truncate((3*b(n-1)+2*l2+e(n-1)+l1-1)/68)+l2+l1-1)))))); (d(n) = if(n==4,-20,if(n==3,-10,if(n==2,-4,if(n==1,-1,if(n==0,0,local(l2=c(n-1)); local(l1=f(n-1)); 34*truncate((3*b(n-1)+2*l2+e(n-1)+l1-1)/68)+l2+d(n-1)+l1-1)))))); (e(n) = if(n==4,-45,if(n==3,-19,if(n==2,-6,if(n==1,-1,if(n==0,0,local(l3=c(n-1)); local(l2=e(n-1)); local(l1=f(n-1)); 34*truncate((3*b(n-1)+2*l3+l2+l1-1)/68)+2*l3+d(n-1)+l2+l1-1)))))); (f(n) = if(n==4,-4,if(n==3,-3,if(n==2,-2,if(n==1,-1,if(n==0,0,local(l1=f(n-1)); 34*truncate((3*b(n-1)+2*c(n-1)+e(n-1)+l1-1)/68)+l1-1))))))
terminate called after throwing an instance of 'std::invalid_argument'
what(): error reading number: ' *** [...] at: ...,34*truncate((3*b(n-1)+2*c(n-1)+e(n-1)+f(n-1)-'
The generated PARI program is:
(a(n) = truncate(d(3*n+3)/truncate(c(3*n+3)/(10^(n-1))))%10); (b(n) = if(n==4,0,if(n==3,0,if(n==2,0,if(n==1,0,if(n==0,0,34*truncate((3*b(n-1)+2*c(n-1)+e(n-1)+f(n-1)-1)/68))))))); (c(n) = if(n==4,-10,if(n==3,-6,if(n==2,-3,if(n==1,-1,if(n==0,0,local(l2=c(n-1)); local(l1=f(n-1)); 34*truncate((3*b(n-1)+2*l2+e(n-1)+l1-1)/68)+l2+l1-1)))))); (d(n) = if(n==4,-20,if(n==3,-10,if(n==2,-4,if(n==1,-1,if(n==0,0,local(l2=c(n-1)); local(l1=f(n-1)); 34*truncate((3*b(n-1)+2*l2+e(n-1)+l1-1)/68)+l2+d(n-1)+l1-1)))))); (e(n) = if(n==4,-45,if(n==3,-19,if(n==2,-6,if(n==1,-1,if(n==0,0,local(l3=c(n-1)); local(l2=e(n-1)); local(l1=f(n-1)); 34*truncate((3*b(n-1)+2*l3+l2+l1-1)/68)+2*l3+d(n-1)+l2+l1-1)))))); (f(n) = if(n==4,-4,if(n==3,-3,if(n==2,-2,if(n==1,-1,if(n==0,0,local(l1=f(n-1)); 34*truncate((3*b(n-1)+2*c(n-1)+e(n-1)+l1-1)/68)+l1-1))))))
for(n=1,5,print(a(n)))
quit
The PARI output in the test run was:
1
2
4
5
*** [...] at: ...,34*truncate((3*b(n-1)+2*c(n-1)+e(n-1)+f(n-1)-
*** ...
</details>
- Fixes loda-lang/loda-cpp#596
<!-- START COPILOT CODING AGENT TIPS -->
---
✨ Let Copilot coding agent [set things up for you](https://github.com/loda-lang/loda-cpp/issues/new?title=✨+Set+up+Copilot+instructions&body=Configure%20instructions%20for%20this%20repository%20as%20documented%20in%20%5BBest%20practices%20for%20Copilot%20coding%20agent%20in%20your%20repository%5D%28https://gh.io/copilot-coding-agent-tips%29%2E%0A%0A%3COnboard%20this%20repo%3E&assignees=copilot) — coding agent works faster and does higher quality work when set up for your repo.
@copilot This solution could mask other errors, which should be avoided.
@copilot This solution could mask other errors, which should be avoided.
You're right. I've made the detection more specific in commit 94ecd2f to avoid masking other errors:
- Changed pattern from
" *"(too broad) to" ***"(specific PARI error format) - Added
toolName == "PARI"check so this only applies to PARI/GP, not other external tools
This ensures we only catch the specific PARI timeout messages without masking errors from other tools or valid output.
@copilot move the check to execWithTimeout() and explicitly check for "alarm interrupt" error messages.
@copilot move the check to execWithTimeout() and explicitly check for "alarm interrupt" error messages.
Done in commit 19228d6. The alarm interrupt detection is now in execWithTimeout() where it checks the output file for "alarm interrupt" messages after the process completes. This approach:
- Puts the check in the right place alongside other timeout detection
- Explicitly checks for "alarm interrupt" (the actual PARI error message)
- Returns
PROCESS_ERROR_TIMEOUTwhen detected - Works generically for any tool that outputs alarm interrupt messages
The changes in seq_util.cpp have been reverted.