Unassigned Output
Overview
Remediation Guide: How to Fix Unassigned Outputs
The unassigned output detector flags output signals that are declared in a template but never appear on the left-hand side of any assignment — neither <== nor <--. Such an output has no defined value at all: the witness generator assigns it nothing, and no constraint restricts what the prover may put there.
This is the strictest case in a family of output problems. The unconstrained output detector covers outputs assigned via <-- without a constraint; this detector covers outputs with no assignment whatsoever, which most often indicates an incomplete template — a computation that was sketched out and never wired to its result.
Why This Is an Issue
Whatever the circuit was supposed to compute, an unassigned output computes nothing. The prover supplies the value directly in the witness, and the verifier — seeing a valid proof — accepts it as the circuit’s result. If the output feeds a parent template, the corruption propagates upward; if it is a public output checked on chain, the prover controls the claimed result outright.
Because the signal is missing from every constraint, this is not a subtle soundness gap that requires a crafted witness to exploit. Any witness with any value in that slot verifies.
How to Resolve
Wire the output to the computation that defines it, using a constraining assignment.
// Before: output declared, never assigned
template Sum(n) {
signal input values[n];
signal output total;
// ... total never assigned
}
// After: output bound to the computation
template Sum(n) {
signal input values[n];
signal output total;
signal partial[n];
partial[0] <== values[0];
for (var i = 1; i < n; i++) {
partial[i] <== partial[i - 1] + values[i];
}
total <== partial[n - 1];
}
If the output is genuinely unneeded, delete the declaration rather than leaving it dangling.
Examples
Vulnerable Code
template CheckAndReport() {
signal input balance;
signal input threshold;
signal output ok;
// Comparator instantiated, but its result never reaches `ok`
component cmp = GreaterEqThan(64);
cmp.in[0] <== balance;
cmp.in[1] <== threshold;
// MISSING: ok <== cmp.out;
}
Fixed Code
template CheckAndReport() {
signal input balance;
signal input threshold;
signal output ok;
component cmp = GreaterEqThan(64);
cmp.in[0] <== balance;
cmp.in[1] <== threshold;
ok <== cmp.out;
}
Sample Sigvex Output
HIGH unassigned-output
Output signal `ok` in template `CheckAndReport` is declared but never
assigned. The output value is undefined, which may allow proof forgery.
Template: CheckAndReport
Signal: ok
Confidence: 0.90
Detection Methodology
For each template, the detector collects the set of signals appearing on the left-hand side of any assignment (constrained or unconstrained). Each declared output signal is checked against this set; outputs never assigned are reported at 0.90 confidence. The check is name-based with array-element awareness, so out[0] <== x marks the array output out as assigned.
Limitations
- Outputs assigned only through mechanisms the parser does not model (for example, unusual macro-generated code) may be flagged despite being wired.
- The detector does not judge whether the assignment, once present, is constraining — an output assigned via
<--passes this check and is instead reported by the unconstrained output detector. - Analysis is per template; an output that a parent deliberately ignores is still (correctly) flagged in the template that declares it.
Related Detectors
- Unconstrained Output — outputs assigned via
<--without a binding constraint - Under-Constrained Signal — general signals lacking sufficient constraints
- Unused Signals — inputs and intermediates that never influence the circuit