1use std::path::PathBuf;
2
3#[derive(Debug, thiserror::Error)]
5pub enum LoaderError {
6 #[error(
7 "What: node {node} ({op_type}) carries opset version {node_version}, but the graph \
8 imports version {graph_version} for domain {domain:?}, and this model cannot be \
9 written out. \
10 Why: a per-node opset is an in-memory IR concept with no representation in ONNX's \
11 protobuf, so serialising would produce a model claiming the wrong operator version \
12 with nothing downstream able to detect it. \
13 How: serialise before the pass that introduced the node-local version, or run with \
14 that fusion disabled."
15 )]
16 NodeVersionNotRepresentable {
17 node: String,
18 op_type: String,
19 domain: String,
20 node_version: i64,
21 graph_version: u64,
22 },
23 #[error("failed to read model file {path}: {source}")]
24 Io {
25 path: PathBuf,
26 #[source]
27 source: std::io::Error,
28 },
29
30 #[error("failed to parse ONNX protobuf: {0}")]
31 ProtobufParse(String),
32
33 #[error("failed to parse ONNX protobuf TextFormat: {0}")]
34 TextProtoParse(String),
35
36 #[error("unsupported opset: domain={domain}, version={version}")]
37 UnsupportedOpset { domain: String, version: u64 },
38
39 #[error(
40 "illegal ONNX model: operator {domain}::{op_type} at node {node} uses domain \
41 '{domain}' but no corresponding opset_import is declared. RULES #1: the model must \
42 declare an opset_import for domain '{domain}'; if you built this graph \
43 programmatically, add it before loading; if this is a file, the model is \
44 malformed/invalid per the ONNX spec"
45 )]
46 MissingOpsetImport {
47 op_type: String,
48 node: String,
49 domain: String,
50 },
51
52 #[error(
53 "illegal ONNX Einsum at node {node}: {detail}. RULES #1: Einsum syntax, schema version, \
54 homogeneous dtype, rank, diagonal, and broadcast constraints must be valid at model \
55 load. Expected: fix the equation/input metadata or export with the applicable ai.onnx \
56 opset (Einsum-12 for opsets 12..27; Einsum-28 for opset 28+)"
57 )]
58 InvalidEinsum { node: String, detail: String },
59
60 #[error(
61 "unsupported ONNX model: operator {domain}::{op_type} at node {node} carries a \
62 subgraph attribute '{attr}' (control-flow / nested-graph op) that this runtime cannot \
63 execute. RULES #1: ep-cpu recursively executes the standard control-flow ops \
64 If/Loop/Scan (ai.onnx), but not {op_type}, so the model cannot be run as-is. \
65 Expected: express control flow with If/Loop/Scan, lower/unroll {op_type} into \
66 supported ops, or register a kernel able to execute its subgraph body"
67 )]
68 UnsupportedControlFlow {
69 op_type: String,
70 node: String,
71 domain: String,
72 attr: String,
73 },
74
75 #[error(
76 "illegal ONNX model: operator {domain}::{op_type} at node {node} consumes tensor \
77 '{tensor}', but no producer exists — it is not a graph input, not an initializer, and \
78 not produced by any upstream node. RULES #1: every consumed tensor must be sourced; \
79 the graph is structurally malformed. Expected: add '{tensor}' as a graph input or \
80 initializer, or add a node that produces it; if this is a file, the model is invalid \
81 per the ONNX spec"
82 )]
83 DanglingTensorRef {
84 op_type: String,
85 node: String,
86 domain: String,
87 tensor: String,
88 },
89
90 #[error(
91 "illegal ONNX model: tensor '{tensor}' is declared as an initializer but is also \
92 produced as an output of node {node} — an initializer must be a constant source with \
93 no producer. RULES #1: initializer names must be unique and must not collide with any \
94 node output name; a producer-backed initializer would let a kernel write through \
95 read-only weight storage. Expected: rename the node output or the initializer so they \
96 no longer share a name; if this is a file, the model is malformed per the ONNX spec"
97 )]
98 InitializerHasProducer { tensor: String, node: String },
99
100 #[error(
101 "illegal ONNX model: value '{tensor}' has multiple producers ({first} and {second}). \
102 RULES #1: ONNX graphs are in SSA form, so a value name may be assigned only once. \
103 Expected: give each graph input and node output a unique name"
104 )]
105 DuplicateValueProducer {
106 tensor: String,
107 first: String,
108 second: String,
109 },
110
111 #[error(
112 "illegal ONNX model: operator {domain}::{op_type} at node {node} has attribute \
113 '{attr}' referring to function attribute '{ref_attr_name}' outside a FunctionProto. \
114 RULES #1: ref_attr_name is only bound while inlining a FunctionProto; it has no \
115 executable value in a main graph or control-flow subgraph. Expected: replace it with \
116 a concrete attribute value or move the node into a FunctionProto"
117 )]
118 RefAttributeOutsideFunction {
119 op_type: String,
120 node: String,
121 domain: String,
122 attr: String,
123 ref_attr_name: String,
124 },
125
126 #[error(
127 "illegal ONNX model: ir_version {ir_version} is invalid. RULES #1: ir_version is \
128 required and ONNX IR versions start at 1. Expected: emit a model with ir_version >= 1"
129 )]
130 InvalidIrVersion { ir_version: i64 },
131
132 #[error(
133 "illegal ONNX model: ir_version {ir_version} requires at least one opset_import \
134 (ONNX IR>=3). Expected: add an opset_import for every operator domain used by the \
135 model"
136 )]
137 MissingModelOpsetImport { ir_version: i64 },
138
139 #[error(
140 "illegal ONNX model: initializer '{tensor}' in an outer graph is shadowed by a \
141 subgraph input of the same name. RULES #1: this runtime does not permit ambiguous \
142 initializer/subgraph binding. Expected: rename the subgraph formal input or the \
143 outer initializer"
144 )]
145 SubgraphInputShadowsInitializer { tensor: String },
146
147 #[error(
148 "illegal ONNX model: graph output '{tensor}' has no producer in its graph. RULES #1: \
149 every output must be a graph input, initializer, or node output in the same scope. \
150 Expected: produce '{tensor}' locally or declare it as an input/initializer"
151 )]
152 GraphOutputMissingProducer { tensor: String },
153
154 #[error("external data file not found: {path}")]
155 ExternalDataNotFound { path: PathBuf },
156
157 #[error("external data path rejected ({reason}): {path}")]
158 ExternalDataPath { path: String, reason: &'static str },
159
160 #[error("weight mmap failed: {0}")]
161 Mmap(String),
162
163 #[error("EPContext node error: {0}")]
164 EpContext(String),
165
166 #[error("EPContext external path rejected ({reason}): {path}")]
167 EpContextPath { path: String, reason: &'static str },
168
169 #[error("graph construction failed: {0}")]
170 GraphBuild(String),
171
172 #[error(
173 "illegal ONNX model: model-local function {function} is recursive (call chain: \
174 {chain}). RULES #1: ONNX function bodies may reference other model-local functions \
175 but MUST NOT be recursive — inlining cannot terminate. Expected: break the cycle so \
176 no function transitively calls itself"
177 )]
178 RecursiveFunction { function: String, chain: String },
179
180 #[error(
181 "illegal ONNX model: call to model-local function {function} at node {node} passes \
182 {actual} {kind}(s) but the function declares only {formal}. RULES #1: a function \
183 call may omit trailing optional {kind}s but must not supply more than are declared. \
184 Expected: remove the extra {kind}(s) or fix the function signature"
185 )]
186 FunctionArityMismatch {
187 function: String,
188 node: String,
189 kind: &'static str,
190 formal: usize,
191 actual: usize,
192 },
193
194 #[error(
195 "illegal ONNX model: call to model-local function {function} at node {node} is missing \
196 required attribute '{attribute}', and the function declares no default for it. \
197 RULES #1: an attribute listed in FunctionProto.attribute has no default and must be \
198 supplied at every call site. Expected: set '{attribute}' on the call node, or give \
199 the function a default via attribute_proto"
200 )]
201 MissingRequiredFunctionAttribute {
202 function: String,
203 node: String,
204 attribute: String,
205 },
206
207 #[error("unsupported ONNX data_type {raw} at {context}")]
208 UnsupportedDataType { raw: i32, context: String },
209
210 #[error("shape inference failed: {0}")]
211 ShapeInference(#[from] onnx_runtime_shape_inference::ShapeInferError),
212
213 #[error(transparent)]
214 Ir(#[from] onnx_runtime_ir::IrError),
215}