open-standards/wai/wai-quantum/docs/qasm-diagnostics.md
Transaction Science 5d5ee28b8c wai: the verdict page, the READMEs and the changelog
docs/verdicts.md gives the ladder, what a verdict records, the intents, the
repair rules and every surface; the reader's diagnostics page points at the
job layer's codes. The crate's, the command line's, the service's, the
Python package's and the Studio's READMEs, and the changelog, describe them.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-27 23:44:27 -04:00

12 KiB

OpenQASM reader: diagnostic codes

Every refusal and warning the reader gives (wai_quantum::qasm), and every construct it lowers, carries or keeps verbatim, has a code. A refusal or a warning reads, on every surface:

line 3: unknown gate foo [Q103]

and, as data (wai-quantum lint --json, the MCP servers' lint_qasm, the job service, the Studio), carries its code, its outcome, its message and where it stands: line, column and length, in characters.

Stability. A code never changes meaning once released. A construct that comes to be supported gets a new code for what now happens to it; the old code keeps its row, marked retired, so a code in an old log or receipt still means what it meant then. New codes are only ever added.

Outcomes.

  • refused: the program is not read, and nothing runs.
  • lowered: read and lowered exactly to the engine's gates and steps.
  • carried: kept in the program as written (its timing, its outputs), changing nothing a method computes unless the method says it uses it.
  • preserved: kept verbatim for export, never executed.
  • warned: read, with something the person should know; the job's result lists it under diagnostics.

What is asked of a program (the request, the method, an intent, a repair, the host) has codes of its own, J…: job-refusals.md. The verdict (verdicts.md) carries a refusal's codes, and the repair (wai-quantum repair, repair_qasm) makes the fixes that are mechanical on the lines a diagnostic flags.

The table is also served as data: wai_quantum::qasm::diag::codes() in Rust, wai-quantum diagnostics [--json] on the command line, the qasm_diagnostics tool of both MCP servers, and engine_diagnostic_codes() in the browser engine. A test holds this page to the code's table.

Code Outcome Meaning
Q001 refused A character that is not part of the language.
Q002 refused A number that does not read: a malformed literal, or one too large to hold.
Q003 refused A string, a block comment, a bracket or a calibration block that is not closed.
Q004 refused Something else was expected here: a ;, a name, an expression or a statement (the message says which).
Q005 refused A statement in a place the language does not allow it: else with no if, case outside switch, a declaration inside a gate.
Q010 warned An OPENQASM version other than 2.x or 3.x: the program is read as OpenQASM 3.
Q011 warned An include of a file other than the standard libraries (stdgates.inc, qelib1.inc): its definitions are not read, so a gate it defines is unknown.
Q012 lowered include "stdgates.inc" or "qelib1.inc": the standard gates, U, gphase and OpenQASM 2's names are built in.
Q013 warned A pragma or annotation the reader does not act on: kept, not applied.
Q100 refused The program declares no qubits.
Q101 refused A name declared twice in one scope, or given to two kinds of thing (a register and an integer).
Q102 refused A name that is not declared: a register, a variable, an alias or a subroutine.
Q103 refused A gate that is neither in the standard library nor defined in the program.
Q104 refused A gate or subroutine given the wrong number of parameters, qubits or arguments.
Q105 refused A declaration only the top level may make (qubits, a gate, a subroutine, an extern) inside a block.
Q106 refused A word of the language, or a built-in constant (pi, tau, euler), used as a name.
Q107 refused An index or slice outside its register or array.
Q108 refused Registers of different lengths in one statement that applies to whole registers.
Q109 refused One qubit named twice in one gate.
Q110 refused A value of the wrong type here: a float where an integer is needed, a complex gate parameter, a qubit where a value is needed.
Q111 refused More qubits than the reader takes (65 536).
Q112 refused An integer outside its type's range (a negative uint, a value too wide for int[n]), or integer arithmetic that overflows.
Q113 refused Division, or a remainder, by zero.
Q114 refused An input given no value, or a value given for an input the program does not declare.
Q115 refused An input of a type the reader takes no value for: only int and uint inputs are given values.
Q116 refused A value that depends on a measurement used where the reader needs it on reading: a gate parameter, an index, a size, a loop's range.
Q117 refused A gate parameter that is not a finite number.
Q118 refused Integers divided with a remainder where a real number is wanted: OpenQASM 3 truncates 1/2 to 0 and OpenQASM 2 reads it as 0.5, so write 1.0/2.
Q119 refused ^ between integers in a program that does not say OPENQASM 3: OpenQASM 3 reads it as exclusive or, OpenQASM 2 as a power; write ** for a power.
Q120 warned ^ between real numbers, read as a power as OpenQASM 2 reads it; OpenQASM 3 writes **.
Q121 warned A value rounded to its declared width, as the language defines (angle[n] to n bits of a turn, float[32] and float[16] to single and half precision); the message gives the amount.
Q122 refused A type width the reader does not hold: a float other than float[16], float[32] and float[64], an integer or an angle wider than 64 bits.
Q123 refused A cast the language does not allow, or one whose value does not fit.
Q124 refused Physical qubits ($0) in a program that also declares qubits.
Q125 refused A call of an extern function, whose body is outside the program.
Q126 refused A call of an opaque gate, or of a gate with only a calibration: its matrix is not known.
Q127 refused An assignment to a constant, an input or a loop variable.
Q128 refused A measurement where a value is needed that is not a bit (measurement gives bits, into a bit register).
Q200 lowered A gate the program defines, inlined at each call with its parameters bound.
Q201 lowered ctrl @ and negctrl @ on any gate, lowered exactly to standard gates (a negative control is a control between two x).
Q202 lowered inv @ on any gate, user gates included: each gate's exact inverse, in reverse order.
Q203 lowered pow(k) @ with an integer k: angles scaled, or the gate repeated; a negative k repeats the inverse.
Q204 lowered pow(r) @ with a fractional r where an exact gate exists: the principal power (pow(1/2) @ x is sx, pow(r) @ rz(θ) is rz(rθ) for |θ| < 2π).
Q205 refused pow(r) @ with a fractional r where no exact gate exists here: a user gate, u, cu, or a rotation by 2π or more.
Q206 lowered gphase(θ): a global phase, which no measurement sees, dropped; under controls it is a phase on the controls.
Q207 refused A control modifier that adds no control: ctrl(0) @.
Q208 refused Modifiers that add more controls than the reader lowers (16).
Q209 refused Gate definitions or subroutine calls nested deeper than 32.
Q210 refused A program that expands to more gates than the reader holds (2 000 000).
Q211 refused A modifier on something that is not a gate.
Q212 refused A statement a gate's body may not hold: a gate holds gate calls, gphase, barrier, loops and conditions known on reading.
Q300 lowered An if, while or switch whose condition is known on reading: decided then.
Q301 lowered A condition on measured bits (c[0], c == 3, c >= 2, &&, ||, !), compiled to steps that branch on them.
Q302 refused A condition that reads neither measured bits nor values known on reading.
Q303 refused A bit register of more than one bit used as a condition: compare it with a number, as c == 3.
Q304 refused A comparison with a number that does not fit in the bits compared.
Q305 refused A negated operand compared with == or != (!c == 1): write c != 1.
Q306 refused Classical arithmetic on measured bits at run time, beyond a condition, or a comparison between two measured values: the program's steps branch on bits and hold no other run-time values.
Q307 refused A condition that reads more than 64 bits.
Q308 lowered for over a range, a set or an array: unrolled, once per value.
Q309 lowered while whose condition is known on reading: unrolled until it fails.
Q310 refused A while whose condition reads measured bits and that declares no bound: add @wai.max_iterations N before it.
Q311 warned A while on measured bits run at most its declared bound (@wai.max_iterations N): a run that still meets its condition after N iterations stops looping there.
Q312 refused A program that does not finish unrolling within the reader's bound (20 000 000 statements and loop iterations).
Q313 refused A range with a zero step, or that does not read as [start:end] or [start:step:end].
Q314 refused break or continue outside a loop, or return outside a subroutine.
Q315 refused A return with a value under a condition on measured bits.
Q316 lowered break, continue, return and end: under a condition on measured bits, a jump forward in the steps.
Q317 lowered switch: decided on reading, or a chain of conditions on measured bits.
Q318 lowered A def subroutine, inlined at each call: classical arguments by value, qubits by reference, its return value given back.
Q319 refused A bit given anything but a measurement at run time: bits are written by measurement (a declaration may give them 0).
Q320 refused A measurement whose result goes nowhere: measure q; while bit registers are declared (write c = measure q;).
Q321 refused Qubits measured into a different number of bits.
Q322 lowered A bit declared inside a block or a subroutine: a bit of its own, which the steps read and the results do not show.
Q400 warned The method applies no durations: the program's delays, boxes and stretches change nothing it computes, and the result says so.
Q401 carried delay[d] on qubits, carried in the program's timing.
Q402 carried box (with or without a duration), carried in the timing around the gates it holds.
Q403 carried stretch: a duration a scheduler would solve, carried by name.
Q404 carried barrier, carried in the timing; it changes no gate.
Q405 refused durationof(…): it needs gate durations, which the program does not give the reader.
Q406 refused A duration that does not read: no unit, negative, or not known on reading.
Q407 carried A gate's duration (x[20ns] q;), carried in the timing, not applied.
Q450 preserved defcal: kept verbatim for export, never executed; the gate's matrix comes from its gate definition or the standard library.
Q451 preserved cal { … }: kept verbatim for export, never executed.
Q452 preserved defcalgrammar: kept verbatim for export.
Q500 refused An angle that is not a dyadic multiple of 2π (2πm/2^k, k ≤ 32): the bit-exact method refuses it, never rounds it; the methods in double precision take it.
Q501 refused More than 255 qubits for the bit-exact circuit.
Q502 refused A program that measures a qubit and then acts on it, resets one after its first gate, or acts on measured bits: the bit-exact circuit takes programs whose measurements all come at the end.
Q600 carried An output declaration, carried into the result: its bits (their places in each outcome) or its value.
Q601 refused An output that is not a bit and whose value depends on a measurement at the end.

See qasm-feature-matrix.md for each construct of OpenQASM 3.1 and its code.