Running inference on an image with PaddleOCR can stop at this NotImplementedError.
NotImplementedError: (Unimplemented) ConvertPirAttribute2RuntimeAttribute not support [pir::ArrayAttribute<pir::DoubleAttribute>] (at /paddle/paddle/fluid/framework/new_executor/instruction/onednn/onednn_instruction.cc:116)
What marks it out is where it happens: PaddleOCR(...) constructs and the models download without complaint, and the failure comes the moment inference runs through predict() (ocr() in the older API). The exact string people search for varies, so here are the parts of it:
- Exception class:
NotImplementedError(the message opens with(Unimplemented)) - Code name:
ConvertPirAttribute2RuntimeAttribute not support - The unsupported attribute type:
[pir::ArrayAttribute<pir::DoubleAttribute>] - The file named as the origin:
new_executor/instruction/onednn/onednn_instruction.cc:116
The conclusion first: there are two ways out. Keep paddle at the latest version and turn oneDNN off with PaddleOCR(enable_mkldnn=False) (the smaller change), or downgrade paddlepaddle to 3.2.2, the version before the regression. Either way paddleocr can stay at 3.5.0.
# Option 1: stay on paddle 3.3.1 and disable oneDNN (one extra argument)
engine = PaddleOCR(enable_mkldnn=False, ...)
# Option 2: to keep the oneDNN speedup, drop back to the version before the regression
pip install "paddlepaddle==3.2.2"
Whichever you pick, confirm what is failing first — the paddle version and the oneDNN path — and choose from there.
Check your paddlepaddle version and oneDNN first
What decides this symptom is not paddleocr but the version of paddlepaddle underneath it — the engine that actually runs the computation — and whether oneDNN, the CPU acceleration layer, is in use. The same paddleocr==3.5.0 behaves differently depending on the combination (all of the following were checked on the same image, on CPU, in python:3.11).
| paddlepaddle | Setting | Running OCR on the same image |
|---|---|---|
| 3.3.1 | default (oneDNN enabled) | stops with NotImplementedError |
| 3.3.1 | enable_mkldnn=False | recognises the text, exit code 0 |
| 3.2.2 | default | also passes |
| 3.1.1 | default | passes (oldest version tried) |
One line shows which version you have.
python -c "import paddle; print(paddle.__version__)"
If that prints 3.3.1 (or 3.3.0, for which upstream has the same report) and you are running on CPU rather than GPU, this article is about your case.
Why it happens in CI and on other machines (paddle’s version sits outside dependency resolution)
paddleocr — and paddlex, which does the inference inside it — does not declare paddlepaddle as a dependency. The design leaves it to you to choose the CPU or the GPU build and the CUDA version, so the execution engine is installed separately. In other words, the paddle version is decided by your base image, your requirements.txt, or a manual pip install — outside dependency resolution.
That is where the environment differences come from. A clean CI job or another machine that re-runs pip install paddlepaddle without a pin picks up whatever is newest at that moment (3.3.1 today) and fails. A development machine that installed 3.1.x or 3.2.x a while back and has been running ever since does not. If the paddle version is not in your logs, this looks like “the same code, but only CI fails”.
Reproduction (minimal)
Take one image with some text in it and run inference with the PP-OCRv5 mobile models. The arguments below are the ones used for the verification run.
# probe.py
from pathlib import Path
from PIL import Image, ImageDraw
from paddleocr import PaddleOCR
image_path = Path("sample.png")
img = Image.new("RGB", (360, 120), "white")
ImageDraw.Draw(img).text((24, 44), "ERRFIX 123", fill="black")
img.save(image_path)
engine = PaddleOCR(
text_detection_model_name="PP-OCRv5_mobile_det",
text_recognition_model_name="PP-OCRv5_mobile_rec",
text_det_limit_type="max",
text_det_limit_side_len=736,
use_doc_orientation_classify=False,
use_doc_unwarping=False,
use_textline_orientation=False,
cpu_threads=1,
)
result = engine.predict(str(image_path))
print(result)
Run this where paddlepaddle==3.3.1 is installed and the NotImplementedError above is raised inside engine.predict(...), in the detection model’s run (predictor.run()); the exit code is 1. Since execution does reach the engine construction (PaddleOCR(...)), you can rule out the model names and the arguments and place the problem at run time.
Fix 1: keep paddle current and turn oneDNN off
What fails is the path that builds instructions for oneDNN. Don’t go through it and the conversion never happens. Passing enable_mkldnn=False to PaddleOCR(...) gets probe.py all the way through on paddlepaddle==3.3.1, with exit code 0.
engine = PaddleOCR(
text_detection_model_name="PP-OCRv5_mobile_det",
text_recognition_model_name="PP-OCRv5_mobile_rec",
enable_mkldnn=False, # add this one line
use_doc_orientation_classify=False,
use_doc_unwarping=False,
use_textline_orientation=False,
cpu_threads=1,
)
In exchange you lose the oneDNN speedup for CPU inference. If CPU processing is then too slow for you, take the downgrade below instead. Note also that the error itself belongs to the CPU + oneDNN path, so it does not occur with GPU inference.
Fix 2: downgrade paddlepaddle to 3.2.2
To keep the oneDNN speedup, drop back to the version before the regression landed. Issue #77340 in PaddlePaddle itself reports this exception as a regression in paddlepaddle 3.3.0 and names 3.2.2 as the version to fall back to. That the same image goes through on 3.2.2 with paddleocr==3.5.0 unchanged was checked here as well.
pip install "paddlepaddle==3.2.2"
On an environment that already has 3.3.1, this command replaces it with 3.2.2. The older 3.1.1 also passes, so it is available as a fallback if 3.2.2 is ruled out by some other dependency.
To keep it from coming back in CI or across a team, pin it in requirements.txt together with the reason. Adding a comment that says the pin is meant to be removed and re-checked against the latest version once the issue is closed prevents the other accident — staying stuck on an old version because everybody forgot why it was pinned.
paddleocr==3.5.0
# paddle 3.3.x raises NotImplementedError in the oneDNN + PIR attribute
# conversion (Paddle issue #77340). Pinned to the version before the
# regression; drop this and re-check on the latest once a fix ships.
paddlepaddle==3.2.2
If you are on the GPU build (paddlepaddle-gpu), the 3.x wheels are not on PyPI and are installed from the official index URL instead. Running the command above (the CPU paddlepaddle) as-is would leave the CPU and GPU builds installed side by side and invite a different problem, so pip uninstall one of them before swapping. What was checked here is the CPU inference path; whether the GPU build raises the same exception was not tried.
Why disabling oneDNN, or downgrading, makes it go away
The ConvertPirAttribute2RuntimeAttribute and pir::... in the message point at the internals of PIR (Paddle Intermediate Representation), paddle’s execution layer. During inference there is a stage that converts the attributes attached to the computation graph into runtime attributes, and paddlepaddle==3.3.1 cannot convert the “array of double” attribute type the model carries (pir::ArrayAttribute<pir::DoubleAttribute>) there, so it raises NotImplementedError. As the location of the failure — onednn_instruction.cc — indicates, this is the path that builds instructions for the oneDNN backend. That is why removing oneDNN with enable_mkldnn=False skips the conversion and passes, and why 3.2.2 and earlier, before the regression, pass too.
This is treated as a problem in paddle’s execution engine, not as a defect in the model. Upstream reports it as a regression in paddlepaddle 3.3.0 (issue #77340), and the same exception is not confined to PaddleOCR: it happens in any setup that runs paddle inference on CPU with oneDNN. The four points measured for errfix are 3.3.1 (fails), 3.3.1 with enable_mkldnn=False (passes), 3.2.2 (passes), and 3.1.1 (passes).
Narrowing it down
- Look at the paddle version and CPU/GPU before anything else: between the environment that fails and the one that works, compare the
paddlepaddleversion (python -c "import paddle; print(paddle.__version__)") and whether inference is on CPU or GPU. Suspect these before thepaddleocrversion. - What has actually been checked: errfix measured the four points in the table above, and upstream issue #77340 reports 3.3.0 and 3.3.1. The whole 3.3 line and any future fixed release were not tested, so rather than assuming “3.3 always fails”, check that the same image passes on the latest version once a fix ships.
- If
pip install paddlepaddleitself fails, that is a different problem: if it stops atNo matching distribution found for paddlepaddleand paddle never installs, the cause is the interpreter, not the execution engine. That is the case where no wheel for that Python exists on PyPI, covered in Fixing “No matching distribution found for paddlepaddle”. - If it fails at import, before OCR starts, that is also a different problem: on minimal Linux and server images (what was verified here is
python:3.11, which is Debian 12 based),libGL.so.1— required by OpenCV — is absent, andpaddleocrstops at import time. That is a missing OS package, not the paddle version. On Debian 12 useapt-get install -y libgl1 libglib2.0-0; on Debian 13 and Ubuntu 24.04 the package name becomeslibglib2.0-0t64. Moving toopencv-python-headless, which does not requirelibGL.so.1at all, is another way out.