fix(pdf-outline): use iterative walk to avoid RecursionError on deep outlines#3855
Open
Ys876 wants to merge 1 commit into
Open
fix(pdf-outline): use iterative walk to avoid RecursionError on deep outlines#3855Ys876 wants to merge 1 commit into
Ys876 wants to merge 1 commit into
Conversation
…outlines extract_outline_from_docling_parse() walked a PDF's bookmark/outline tree recursively, one Python function call per nesting level, with no depth limit. PDFs whose outline is nested deeper than Python's default recursion limit (1000) -- which happens for real, large technical or legal documents with many nested heading levels, and can happen more severely in malformed PDFs -- crashed with RecursionError. Replace the recursive walk with an iterative one using an explicit stack. Verified output-equivalent to the original across 200 randomized tree shapes plus explicit edge cases (empty tree, flat list, whitespace titles), and verified the fix handles trees far deeper than the original could ever survive (tested to 100,000 levels). Added tests/test_pdf_outline.py, which had no prior coverage for this module in isolation -- the existing heading-hierarchy tests only use shallow real sample PDFs and could never have caught a deep-recursion bug. Fixes docling-project#1743 Signed-off-by: Yana shah <shah1115@purdue.edu>
Contributor
|
✅ DCO Check Passed Thanks @Ys876, all your commits are properly signed off. 🎉 |
Contributor
Merge Protections🟢 Merge protection satisfied — ready to merge. Show 1 satisfied protection🟢 Enforce conventional commitMake sure that we follow https://www.conventionalcommits.org/en/v1.0.0/
|
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
cau-git
approved these changes
Jul 24, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Fixes #1743. PDFs whose bookmark/outline tree is nested deeper than
Python's default recursion limit (1000 levels) crashed with
RecursionError: maximum recursion depth exceededduring conversion.This happens for real on large technical or legal documents with many
nested heading levels, and can happen more severely with malformed
PDFs.
Root cause
extract_outline_from_docling_parse()indocling/utils/pdf_outline.pywalked the outline tree with a plain recursive function, one Python
call per nesting level, with no depth bound.
Fix
Replace the recursive walk with an iterative pre-order depth-first
traversal using an explicit stack -- a standard technique for removing
Python's call-stack limit from tree traversals.
Testing
implementation across 200 randomly generated tree shapes (varying
size and branching), plus explicit edge cases: empty tree, flat
sibling list, a 500-deep chain (shallow enough for the original to
survive, for direct side-by-side comparison), and title-whitespace
handling (empty, whitespace-only, needs-stripping).
tested successfully to 100,000 nested levels.
tests/test_pdf_outline.py(previously zero coverage for thismodule in isolation) with 5 tests, including a regression test that
fails with
RecursionErroragainst the unmodified code and passesagainst the fix.
tests/test_heading_hierarchy_bookmarks.pysuite(13 tests, including two against real sample/generated PDFs) --
all pass unmodified, zero regression.
ruff format/ruff checkboth report the changed files alreadyclean.