Skip to content

fix(layout): fall back to the uncompiled model when torch.compile fails (#3956) - #3964

Open
Anai-Guo wants to merge 1 commit into
docling-project:mainfrom
Anai-Guo:fix/compile-fallback-object-detection
Open

fix(layout): fall back to the uncompiled model when torch.compile fails (#3956)#3964
Anai-Guo wants to merge 1 commit into
docling-project:mainfrom
Anai-Guo:fix/compile-fallback-object-detection

Conversation

@Anai-Guo

@Anai-Guo Anai-Guo commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

Related to #3956 (and complementary to #3962, which changes the default rather than the failure mode).

The problem

torch.compile() is lazy. It returns an OptimizedModule immediately and the backend only builds a kernel on the first forward pass. In TransformersObjectDetectionEngine the compile call sits inside initialize()'s try/except, but the first forward happens in predict_batch():

with torch.inference_mode():
    outputs = self._model(**inputs)

Nothing catches it there, so on a machine without a working C++ compiler the conversion dies with torch._dynamo.exc.BackendCompilerFailed wrapping InvalidCxxCompiler, surfaced to the user as a ConversionError.

Since #3914 made LayoutObjectDetectionOptions the default and compile_torch_models defaults to True, this sits on the default PDF path — a plain DocumentConverter().convert(...) raises. It is not Windows-specific: it reproduces the same way on a slim Linux image with no g++ (reported against python:3.12-slim in neuml/txtai#1176, where it also broke that project's Windows CI).

The change

Compilation is an optimization, not a correctness requirement, so keep a handle on the uncompiled module and degrade to it with a warning instead of failing the conversion:

  • initialize() records self._uncompiled_model before handing the model to torch.compile().
  • A small _forward() helper wraps the call. On torch._dynamo.exc.TorchDynamoException it logs a warning naming the env var, swaps _model back to the uncompiled module, and retries once.
  • The swap is permanent, so subsequent batches do not pay the failed-compile cost again.

Deliberately narrow: if no uncompiled handle was recorded (compile_model=False, so nothing was compiled), the error propagates unchanged; anything that is not a TorchDynamoException propagates unchanged, so a genuine model error is never masked as a compile problem.

Verification

torch.compile() really does defer the failure to the first forward — confirmed on torch 2.5.1, Python 3.12, Windows with no cl/g++ on PATH:

compile() returned OK: OptimizedModule
FORWARD FAILED at first call -> torch._dynamo.exc.BackendCompilerFailed
MRO: [BackendCompilerFailed, TorchDynamoException, RuntimeError, Exception, ...]

Against a genuinely failing compile on that machine:

before after
first forward raises BackendCompilerFailedConversionError warns, falls back, returns output
genuine model error propagates propagates (unchanged)
nothing to fall back to raises raises (unchanged)

tests/test_object_detection_compile_fallback.py covers those three rows and needs no model download. It fails 3/3 against the code before this change and passes 3/3 after.

ruff==0.15.12 check and format --check clean with the repo config.

Notes

🤖 Generated with Claude Code

torch.compile() is lazy: the backend only builds a kernel on the first
forward pass, so on a machine without a working C++ compiler the failure
surfaces in predict_batch() rather than in initialize(). Nothing catches
it there, so it propagates as a ConversionError and the whole conversion
fails.

Since docling-project#3914 made LayoutObjectDetectionOptions the default and
compile_torch_models defaults to True, this is on the default PDF path,
so a plain convert() raises on any environment without a compiler --
Windows without MSVC, but also slim Linux images without g++.

Compilation is an optimization, so keep a handle on the uncompiled model
and degrade to it with a warning instead of failing the conversion.
The swap is permanent, so later batches do not retry compilation. A
missing handle, and errors that are not TorchDynamoException, still
propagate unchanged.

Signed-off-by: Tai An <antai12232931@outlook.com>
@github-actions

github-actions Bot commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

DCO Check Passed

Thanks @Anai-Guo, all your commits are properly signed off. 🎉

@mergify

mergify Bot commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

Merge Protections

🟢 Merge protection satisfied — ready to merge.

Show 1 satisfied protection

🟢 Enforce conventional commit

Make sure that we follow https://www.conventionalcommits.org/en/v1.0.0/

  • title ~= ^(fix|feat|docs|style|refactor|perf|test|build|ci|chore|revert)(?:\(.+\))?(!)?:

@codecov

codecov Bot commented Aug 10, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 91.66667% with 1 line in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
...ce_engines/object_detection/transformers_engine.py 91.66% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant