stride3d/stride · error · NotImplementedException
Couldn't handle operand
Error message
Couldn't handle operand {logOp} What it means
OpDataEnumerator.MoveNext advances word/operand/pattern indices per the SPIR-V grammar's operand quantifiers (One, ZeroOrOne, ZeroOrMore). The switch covers all quantifier kinds it knows; an operand shape not in the table hits the throw. This indicates either an unhandled combination of Quantifier and operand-position state or corrupted/unknown operand metadata.
Solutions
- Log logOp (name, quantifier, kind) at the throw site to identify the unhandled combination and add a matching switch arm.
- Update the library's operand grammar tables to match the SPIR-V version of the module being parsed.
- Validate the input SPIR-V with spirv-val to rule out a malformed module driving the enumerator off-spec.
- As a workaround, parse the failing opcode manually instead of via OpDataEnumerator.
Example fix
// before
_ => throw new NotImplementedException($"Couldn't handle operand {logOp}")
// after
{ Quantifier: OperandQuantifier.ZeroOrMore, Kind: OperandKind.LiteralString } => /* handle strings */,
_ => throw new NotImplementedException($"Couldn't handle operand {logOp}") Defensive patterns
Strategy: try-catch
Validate before calling
// validate the module first with spirv-val (external) and check opcode support:
if (!opData.KnownOpcodes.Contains((Op)words[0] & 0xFFFF)) throw new NotSupportedException("Unknown opcode before enumeration"); Type guard
null
Try / catch
try { while (enum.MoveNext()) ProcessCurrent(); }
catch (NotImplementedException ex) when (ex.Message.StartsWith("Couldn't handle operand")) { /* fall back to manual parsing of this opcode; log the operand descriptor */ } Prevention
- Keep the library's grammar tables in sync with the SPIR-V version your modules use.
- Run spirv-val on input modules before parsing.
- Log the failing opcode/operand descriptor to add missing switch arms quickly.
When it happens
Trigger: Iterating operands of an instruction whose operand descriptor has a Quantifier/Kind combination not covered by the MoveNext switch — e.g. ZeroOrMore appearing not-last, or unexpected logOp state — while consuming Op instruction words.
Common situations: Parsing SPIR-V from a newer grammar version (new operand kinds in spirv.core.grammar.json) than the library's embedded OpData tables; malformed/custom instruction streams; regenerated operand tables missing a case.
Related errors
- Couldn't handle operand kind
- kind is inexistent
- Invalid SPIR-V instruction
- params of strings is not yet implemented
- params of strings is not yet implemented
AI-assisted analysis of stride3d/stride@96fad776d2 (2026-09-14).
Data as JSON: /api/errors/fe1732a7fa3ccedd.
Report an issue: GitHub.
Appendix: source
Thrown at sources/shaders/Stride.Shaders.Spirv/Parsing/OpDataEnumerator.cs:118
operands[pid] switch
{
{ Kind: OperandKind.PairIdRefIdRef or OperandKind.PairIdRefLiteralInteger or OperandKind.PairLiteralIntegerIdRef } => (wid + 2, oid, pid + 1, startOperand),
{ Kind: OperandKind.LiteralString } => (wid + Operands[wid..].LengthOfString(), oid, pid + 1, startOperand),
{ Kind: _ } => (wid + 1, oid, pid + 1, startOperand)
},
{ Quantifier: OperandQuantifier.One, Kind: OperandKind.PairIdRefIdRef or OperandKind.PairIdRefLiteralInteger or OperandKind.PairLiteralIntegerIdRef }
=> (wid + 2, oid + 1, -1, -1),
{ Quantifier: OperandQuantifier.One, Kind: OperandKind.LiteralString } => (wid + Operands[wid..].LengthOfString(), oid + 1, -1, -1),
{ Quantifier: OperandQuantifier.One, Kind: _ } => (wid + 1, oid + 1, -1, -1),
{ Quantifier: OperandQuantifier.ZeroOrOne, Kind: OperandKind.PairIdRefIdRef or OperandKind.PairIdRefLiteralInteger or OperandKind.PairLiteralIntegerIdRef }
=> (wid + (wid < Operands.Length - 2 ? 2 : 0), oid + (wid < Operands.Length - 2 ? 1 : 0), -1, -1),
{ Quantifier: OperandQuantifier.ZeroOrOne, Kind: OperandKind.LiteralString }
=> (wid + (wid < Operands.Length - 1 ? Operands[wid..].LengthOfString() : 0), oid + (wid < Operands.Length ? 1 : 0), -1, -1),
{ Quantifier: OperandQuantifier.ZeroOrOne, Kind: _ }
=> (wid + (wid < Operands.Length ? 1 : 0), oid + (wid < Operands.Length ? 1 : 0), -1, -1),
{ Quantifier: OperandQuantifier.ZeroOrMore }
=> (wid < Operands.Length - 1 ? Operands.Length : wid, oid + (wid < Operands.Length - 1 ? 0 : 1), -1, -1),
_ => throw new NotImplementedException($"Couldn't handle operand {logOp}")
};
wid = newWid;
oid = newOid;
pid = newPid;
// Reasons to return false :
// - no operands left
// - current operand has no kind (i.e. None)
return !(wid >= Operands.Length || oid >= logicalOperands.Count);
}
}
public SpvOperand ParseCurrent()
{
var logOp = logicalOperands[oid];
return logOp switch
{View on GitHub (pinned to 96fad776d2)