stride3d/stride · error · InvalidOperationException
Invalid SPIR-V instruction
Error message
Invalid SPIR-V instruction
What it means
Thrown during SpirvProbe's instruction walk when an instruction header declares a word count of zero. SPIR-V requires every instruction's high 16 bits (wordCount) to be at least 1 (the opcode word itself); a zero means the module is corrupt or the parse position has desynchronized.
Solutions
- Regenerate or re-download the SPIR-V module — the file is truncated/corrupted; compare checksums against the build output.
- Validate the module with spirv-val before probing to pinpoint where the layout breaks.
- Re-run the shader compiler without lossy post-processing steps that rewrite instruction sizes.
Example fix
// before
var probe = new SpirvProbe(truncatedBytes);
// after
if (!SpirvValidator.IsValid(truncatedBytes)) // e.g. spirv-val
throw new InvalidOperationException("SPIR-V module is corrupt; recompile shader");
var probe = new SpirvProbe(truncatedBytes); Defensive patterns
Strategy: validation
Validate before calling
static bool HasValidLayout(ReadOnlySpan<byte> spirv)
{
var words = MemoryMarshal.Cast<byte, uint>(spirv);
if (words.Length < 5) return false;
int i = 5;
while (i < words.Length)
{
int wc = (int)(words[i] >> 16);
if (wc == 0) return false;
i += wc;
}
return i == words.Length;
} Try / catch
try { var probe = new SpirvProbe(bytes); }
catch (InvalidOperationException ex) when (ex.Message == "Invalid SPIR-V instruction")
{
// module is corrupt/truncated — recompile or re-fetch the artifact
} Prevention
- Run spirv-val on generated modules in CI before use.
- Avoid lossy byte-level post-processing of SPIR-V (strippers/obfuscators) without revalidation.
- Verify artifact integrity (size/checksum) when shaders come from network or shared storage.
When it happens
Trigger: Parsing a truncated or corrupted .spv file where the offset lands on a word whose upper 16 bits are 0; manually patched/relocated SPIR-V that broke instruction boundaries; a header-valid module whose body was cut short so trailing garbage is read as a header.
Common situations: Incomplete file downloads or partial writes producing truncated SPIR-V; byte-level post-processing (e.g. stripping or obfuscation tools) corrupting instruction lengths; hand-crafted fuzz inputs in tests.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- Couldn't handle operand kind
- Couldn't handle operand
- kind is inexistent
- 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/72b48b911794ea54.
Report an issue: GitHub.
Appendix: source
Thrown at sources/shaders/Stride.Shaders.Tests/SpirvProbe.cs:52
public List<EntryPointInfo> EntryPoints { get; } = [];
public Dictionary<uint, uint> Locations { get; } = [];
public Dictionary<uint, string> UserSemantics { get; } = [];
public HashSet<uint> BuiltIns { get; } = [];
public Dictionary<uint, uint> VariableStorageClasses { get; } = [];
public SpirvProbe(ReadOnlySpan<byte> spirv)
{
var words = MemoryMarshal.Cast<byte, uint>(spirv);
if (words.Length < 5 || words[0] != 0x07230203)
throw new InvalidOperationException("Not a SPIR-V module");
for (int i = 5; i < words.Length;)
{
var wordCount = (int)(words[i] >> 16);
var opcode = words[i] & 0xFFFF;
if (wordCount == 0)
throw new InvalidOperationException("Invalid SPIR-V instruction");
var operands = words.Slice(i + 1, wordCount - 1);
switch (opcode)
{
case OpEntryPoint:
{
var name = ReadString(operands.Slice(2), out var nameWords);
EntryPoints.Add(new EntryPointInfo(operands[0], name, operands.Slice(2 + nameWords).ToArray()));
break;
}
case OpVariable:
VariableStorageClasses[operands[1]] = operands[2];
break;
case OpDecorate:
switch (operands[1])
{
case DecorationLocation:
Locations[operands[0]] = operands[2];View on GitHub (pinned to 96fad776d2)