ONNX Doctor
An extensible linter for ONNX models — like ruff, but for ONNX. Catch spec violations, compatibility issues, and common pitfalls with clear messages and actionable suggestions.
Installation
pip install onnx-doctor
Quick Start
CLI
Check a model for issues:
onnx-doctor check model.onnx
Example output:
model.onnx:graph: ONNX001 Graph name is empty.
suggestion: Set the name of the graph, e.g. `graph.name = 'main_graph'`.
model.onnx:graph:node/5(MyCustomOp): ONNX019 No schema found for '::MyCustomOp' at opset version 21.
suggestion: Verify the operator name, domain, and imported opset version.
model.onnx:graph:node/5(MyCustomOp): ONNX020 Value 'custom_out' has no type annotation.
suggestion: Run shape inference on the model, e.g. `onnx.shape_inference.infer_shapes(model)`.
Found 2 errors, 1 warning.
The location path shows where the issue is in the graph hierarchy. For nodes,
it includes the node index and name (e.g. node/5(MyCustomOp)). For values,
it shows the producer node. For subgraphs, the full path is shown:
model.onnx:graph:node/3(If_0):then_branch:node/1(Add): ONNX019 ...
Programmatic API
import onnx_ir as ir
import onnx_doctor
from onnx_doctor.diagnostics_providers import OnnxSpecProvider
model = ir.load("model.onnx")
messages = onnx_doctor.diagnose(model, [OnnxSpecProvider()])
for msg in messages:
print(f"[{msg.severity}] {msg.error_code}: {msg.message}")
CLI Reference
onnx-doctor <command> [options]
check — Lint an ONNX model
onnx-doctor check model.onnx [options]
| Option | Description |
|---|---|
--select CODE [...] |
Only report rules matching these codes or prefixes (e.g. ONNX001, ONNX). |
--ignore CODE [...] |
Ignore rules matching these codes or prefixes. |
--output-format {text,json,github} |
Output format. Default: text. |
--severity {error,warning,info} |
Minimum severity to report. |
--fix |
Apply available fixes and save the model. |
-o, --output PATH |
Output path for the fixed model (default: overwrite input). Only used with --fix. |
--diff |
Show a unified diff of what --fix would change, without writing. |
--ort |
Enable ONNX Runtime compatibility checks (ORT rules). |
--ort-provider NAME |
Execution provider for ORT checks (default: CPUExecutionProvider). |
Exit codes: 0 = no errors (warnings may be present), 1 = errors found.
Examples:
# Ignore ir-version warnings
onnx-doctor check model.onnx --ignore ONNX013
# Only show errors
onnx-doctor check model.onnx --severity error
# Apply auto-fixes in place
onnx-doctor check model.onnx --fix
# Apply auto-fixes to a new file
onnx-doctor check model.onnx --fix -o fixed_model.onnx
# Preview what --fix would change
onnx-doctor check model.onnx --diff
# Enable ORT compatibility checks
onnx-doctor check model.onnx --ort
# JSON output for CI
onnx-doctor check model.onnx --output-format json
# GitHub Actions annotations
onnx-doctor check model.onnx --output-format github
JSON output example:
[
{
"file": "model.onnx",
"code": "ONNX001",
"severity": "error",
"message": "Graph name of the root graph is empty.",
"target_type": "graph",
"location": "graph",
"rule_name": "empty-graph-name",
"suggestion": "Set the name of the graph, e.g. `graph.name = 'main_graph'`."
}
]
explain — Show rule details
onnx-doctor explain ONNX001
ONNX001: empty-graph-name
Message: Graph name of the root graph is empty.
Severity: error
Category: spec
Target: graph
Suggestion: Set the name of the graph, e.g. `graph.name = 'main_graph'`.
## Details
The 'name' field of a graph must not be empty per the ONNX spec.
You can also look up rules by name:
onnx-doctor explain empty-graph-name
list-rules — Show all available rules
onnx-doctor list-rules
Rules
ONNX Doctor ships with 63 built-in rules across five providers:
| Prefix | Provider | Description |
|---|---|---|
ONNX |
ONNX Spec | 42 rules for ONNX spec compliance (graph, model, node, value, tensor, function) |
PB |
Protobuf | 13 rules for protobuf-specific issues |
SIM |
Simplification | 3 rules for removing unused elements (functions, opsets, nodes) |
ORT |
ORT Compatibility | 5 rules for ONNX Runtime compatibility checks (opt-in via --ort) |
SP |
Sparsity | Tensor sparsity analysis (example provider, not enabled by default) |
Writing Custom Providers
Create your own rules by subclassing DiagnosticsProvider. Each provider
implements a diagnose(model) method that walks the model and yields messages:
import onnx_ir as ir
import onnx_doctor
class MyProvider(onnx_doctor.DiagnosticsProvider):
def diagnose(self, model: ir.Model):
# Walk the graph yourself — you know best how to traverse it
for node in ir.traversal.RecursiveGraphIterator(model.graph):
if some_condition(node):
yield onnx_doctor.DiagnosticsMessage(
target_type="node",
target=node,
message=f"Issue with node {node.op_type}.",
severity="warning",
producer="MyProvider",
error_code="CUSTOM001",
)
# Check graph-level properties
node_count = sum(1 for _ in model.graph)
if node_count > 1000:
yield onnx_doctor.DiagnosticsMessage(
target_type="graph",
target=model.graph,
message=f"Graph has {node_count} nodes — consider optimizing.",
severity="warning",
producer="MyProvider",
error_code="CUSTOM002",
)
model = ir.load("model.onnx")
messages = onnx_doctor.diagnose(model, [MyProvider()])
License
Metadata
Release files for onnx-doctor 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| onnx_doctor-0.2.0.tar.gz | 54.7 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| onnx_doctor-0.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 117.0 kB
Release files / onnx_doctor-0.2.0.tar.gz
| Download URL | onnx_doctor-0.2.0.tar.gz |
|---|---|
| Size | 54.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
fa3b01d49b1509d17decda14a22f57186790504bdbf442aaa87eab8bddba1b57
|
|
BLAKE2b-256 checksum How to use checksums |
cb6273deb39b95db2afb5e2bf28fc261b78e461613edd1ae7242d395d625e2ef
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.7
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Feb 14, 2026.
Transparency logRelease files / onnx_doctor-0.2.0-py3-none-any.whl
| Download URL | onnx_doctor-0.2.0-py3-none-any.whl |
|---|---|
| Size | 62.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
cc00bb36d2feb5de126d9d7f7278b85a21eb827ce350cb3952bffe93a914cd41
|
|
BLAKE2b-256 checksum How to use checksums |
7a6d75064d4364b37c801fae48a75c83a999ee75e245e115807b4b7ec6e6a96f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.7
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Feb 14, 2026.
Transparency log