how-to Intermediate

How to Handle Compilation Failure

Compilation failure in pyvorin-native 1.0.9 almost never means a crash: the verified taxonomy, what each status means, and how to get per-function reasons.

Published Apr 1, 2026

Compilation failure in Pyvorin Native rarely looks like a failure. There is no stack trace to chase and no crashed process: the compiler classifies each function, runs the ones it can natively, and routes the rest onto an honest CPython fallback that executes the original source unchanged. The practical skill is reading that classification quickly, finding the per-function reason, and knowing which cases deserve a code change and which deserve a shrug. This page is the verified taxonomy for pyvorin-native 1.0.9, built from commands we ran against the installed build.

The older version of this page assumed a remote compile pipeline with job details and failure stages called parse, validate, link and artifact. None of that exists in the product. Compilation is local and synchronous, and the diagnosis commands are support, compile --explain-fallback, explain and cache status — all verified below.

The four outcomes

Every function the compiler examines lands in one of four states, and the support report shows all of them in one screen:

python -m pyvorin support your_file.py
Support report for your_file.py
Function                       Status               Unsupported
------------------------------------------------------------
square_sum                     COMPILED_FULL        0
with_generator                 COMPATIBILITY_EXECUTED 0
StatusWhat happenedWhat to do
COMPILED_FULLThe entire function lowered to native machine code with zero unsupported constructs.Nothing. Benchmark it.
COMPILED_PARTIALThe function compiles, but named constructs stay on the interpreter path; execution continues in compatibility mode for those parts.Read the reason, judge whether the unsupported construct is on the hot path, rewrite it if so.
COMPATIBILITY_EXECUTEDThe function ran as ordinary CPython through the fallback path.Read the reason. Either the function is not a native candidate, or something it calls isn't.
FAILEDThe compile or the oracle validation raised an error, and the function is not safe to claim as compiled.Read the error line in explain output; fix and retry.

The fourth row deserves a word of precision. In our runs, a FAILED status from explain carried an explicit error line and Public claim allowed: False — the tooling is refusing to let you cite that function as compiled until the error is resolved. That conservative posture is the pattern across the whole failure model: when in doubt, the system runs the original Python rather than risking a wrong answer.

Getting the per-function reason

Two commands drill into the reasons. The first attaches to compile:

python -m pyvorin compile partial.py --explain-fallback
with_generator:
  Fallback reasons:
    - unsupported_ast_pattern (with_generator): unsupported AST pattern; calls a compat-mode function
entry:
  Fallback reasons:
    - unsupported_ast_pattern (entry): calls a compat-mode function

The reasons are causal chains, and they read like them: entry fell back because it calls with_generator, which fell back because of an unsupported AST pattern. One bad function drags its callers onto the compatibility path, so fix the root, not the leaves.

The second command, explain, gives the deepest per-function view, including the named unsupported construct. A generator function using yield produced this real capture:

python -m pyvorin explain gen2.py
  yielder
    Status:  COMPILED_PARTIAL
    Compile: 0.000 ms
    Oracle:  needs_args
    Public claim allowed: False
    Error:   Runtime validation not performed: function requires arguments
    Unsupported features:
      - Yield (yielder): Unsupported feature encountered; function executes in compatibility mode

The Oracle field explains the error line: functions that require arguments cannot be executed against the CPython ground truth during compilation, so runtime validation is skipped and the function cannot be claimed as publicly verified. Give the file a zero-argument entrypoint and the oracle runs. The Compile: 0.000 ms on that run is the disk cache serving a previously compiled artefact — repeat diagnoses are free.

Turning fallback into a hard error

In development, silent fallback is a convenience. In CI, it is a loophole — a function you believe is compiled may be running as interpreted CPython while your pipeline stays green. The compile command has a flag for exactly this, verified during the writing of this page:

python -m pyvorin compile partial.py --fail-on-fallback
Compile: partial.py
  with_generator                 COMPATIBILITY_EXECUTED 42.732 ms | Runtime validation not performed: function requires arguments
  entry                          COMPATIBILITY_EXECUTED 31.155 ms
exit=2

Exit code 2, and the job fails. That single flag converts the whole failure taxonomy into a gate. If your team is adopting Pyvorin function by function, wire it into the pre-merge check for the files you have already claimed as native; a regression to fallback then breaks the build instead of quietly costing you the speedup you were measuring.

Runtime failure: GuardRuntimeError and what it means

Not every failure happens at compile time. Native code runs on assumptions: the compiler assumed an argument's dtype, its memory layout, its arity. Guards check those assumptions at the boundary before the kernel runs, and the failure type they raise is GuardRuntimeError, carrying a structured GuardFailure record naming the guard, a human-readable reason and the argument involved. We read this directly from the installed package's FFI layer. A simplified view of the logic:

if len(args) != len(plan.args):
    if python_reference is not None:
        return python_reference(*args)   # divert to fallback: original Python
    raise GuardRuntimeError(
        GuardFailure("arity", f"Expected {len(plan.args)} args, got {len(args)}")
    )

The semantics matter more than the mechanics. When a guard fails and a Python reference implementation is available, the call diverts to the original source — the fallback — with the same arguments, and that result is returned. Semantics are preserved exactly, because the fallback is the original program. When no fallback is available, the error propagates loudly instead of guessing. Guard checks cover arity, dtype match, C-contiguous layout, non-null data pointers and non-aliasing between arrays the kernel assumed were independent.

There is one deliberate exception to silent diversion, and it is principled: when native code has already mutated parameter state that a CPython rerun would double-apply, the wrapper re-raises rather than reruns. A partially applied mutation cannot be replayed honestly, so the system stops loudly instead of corrupting state. The full guard and deoptimisation contract, including why failed guards do not trigger recompilation in 1.0.9, is on the guards and deoptimisation page.

Inspecting the compile cache

Sometimes the failure is not in your code but in your state — a corrupted artefact, a cache you expected to hit and didn't, or stale entries after an upgrade. The product CLI's cache commands are verified to run:

python -m pyvorin cache --help
python -m pyvorin cache status
usage: pyvorin cache [-h] [{status,clear}]

positional arguments:
  {status,clear}  Cache action: status (default) shows file count and disk
                  usage; clear removes the cache
Cache directory: /root/.pyvorin_cache
Total files: 172
Total size: 1.35 MB

Know what these commands manage before you reach for them. cache status reports on ~/.pyvorin_cache — the user cache that appears at import time. cache clear empties that directory, and we verified it leaves the compile artefact cache in the install tree untouched. The compiled machine code itself lives at <site-packages>/.pyvorin_cache/disk_compile, as SHA256-named shared objects with a cache_index.json index, an LRU capped at 500 entries and per-entry checksum validation. If you suspect a corrupt artefact, the honest check is the checksum: invalid entries are detected on load, and editing your source invalidates the key anyway, because the key hashes the source along with the function name, options and toolchain modification times. For cache behaviour inside build pipelines, see caching compiled artifacts in CI.

A worked judgement call

Suppose explain tells you a hot ETL function stays in compatibility mode because it builds a generator of log lines once per batch. The constructs are the diagnosis; the judgement is yours. Our rule of thumb, formed from running these tools: if the unsupported construct is on the cold path — setup, error handling, a log preamble — leave the code alone and take the partial win. If it is inside the loop that dominates your profile, rewrite the construct, because the fallback exists to keep you correct, not to save you the edit. A generator expression becomes a list comprehension in seconds; a fallback that hides that fact costs you the entire point of the compiler. Correctness is the system's job. Deciding what your hot path should look like remains yours.

Where to go next

Last reviewed 1 April 2026 against pyvorin-native 1.0.9 installed at /root/pvfinal. The explain, compile --explain-fallback, --fail-on-fallback exit code and cache status output shown are captured from real runs; GuardRuntimeError semantics were read from the installed FFI layer and router source.