stride3d/stride · error · InvalidOperationException

SPIRV Magic number not found

Error message

SPIRV Magic number not found

What it means

ShaderBuffers.CreateFromSpan parses a SPIR-V binary module from an int span. The first word of a SPIR-V module must be the Specification.MagicNumber (0x07230203); if it is not, the method throws InvalidOperationException('SPIRV Magic number not found'), meaning the input is not a valid SPIR-V module (or the span is misaligned/shifted).

Solutions

  1. Verify the input actually contains a SPIR-V module (first 4 bytes = 03 02 23 07 little-endian)
  2. If the blob has a wrapper, skip to the correct offset before calling CreateFromSpan
  3. Check endianness when converting raw bytes to Span<int>; swap bytes if the producer was big-endian

Example fix

// before
var buffers = ShaderBuffers.CreateFromSpan(bytes.AsSpan().Cast<byte, int>());
// after
if (BitConverter.IsLittleEndian && bytes[0] == 0x03 && bytes[1] == 0x02 && bytes[2] == 0x23 && bytes[3] == 0x07)
    var buffers = ShaderBuffers.CreateFromSpan(MemoryMarshal.Cast<byte, int>(bytes));
Defensive patterns

Strategy: validation

Validate before calling

const int SpirvMagic = 0x07230203;
bool LooksLikeSpirv(ReadOnlySpan<int> span) => span.Length >= 5 && span[0] == SpirvMagic;
if (!LooksLikeSpirv(span)) throw new InvalidOperationException("Input is not a SPIR-V module (bad magic number)");

Type guard

static bool IsSpirvBinary(ReadOnlySpan<byte> bytes) => bytes.Length >= 4 && bytes[0] == 0x03 && bytes[1] == 0x02 && bytes[2] == 0x23 && bytes[3] == 0x07;

Try / catch

try { var buffers = ShaderBuffers.CreateFromSpan(span); }
catch (InvalidOperationException ex) when (ex.Message.Contains("Magic number")) { Log("Not a SPIR-V module: " + ex.Message); return null; }

Prevention

When it happens

Trigger: Passing a span whose first int is not 0x07230203 — e.g. a GLSL/assembly text buffer, a SPIR-V blob with a prepended header, byte-order-swapped data from an endianness mismatch, or an offset-into-module span starting past the header.

Common situations: Reading compiled SPIR-V from a custom container that stores extra metadata before the module; loading DXIL/other shaders by mistake; converting byte[] to int[] with wrong endianness (SPIR-V is little-endian on LE hosts).

Related errors


AI-assisted analysis of stride3d/stride@96fad776d2 (2026-09-14). Data as JSON: /api/errors/b181f678d7c8399f. Report an issue: GitHub.

Appendix: source

Thrown at sources/shaders/Stride.Shaders.Parsers/Spirv/Building/Builder.Class.cs:34

using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using Stride.Core.Storage;
using static Stride.Shaders.Spirv.Specification;
using Stride.Shaders.Spirv.Core.Parsing;
using System.Diagnostics.CodeAnalysis;

namespace Stride.Shaders.Spirv.Building;

public record class ShaderMixinInstantiation(List<ShaderClassInstantiation> Mixins, Dictionary<string, ShaderMixinInstantiation[]> Compositions);

public record struct ShaderBuffers(SpirvContext Context, SpirvBuffer Buffer)
{
    public static ShaderBuffers CreateFromSpan(Span<int> span)
    {
        if (span[0] != Specification.MagicNumber)
            throw new InvalidOperationException("SPIRV Magic number not found");

        var header = SpirvHeader.Read(span);

        var context = new SpirvContext();
        var buffer = new SpirvBuffer();

        int wid = SpirvHeader.IntSpanSize;
        bool isContext = true;
        while (wid < span.Length)
        {
            var instruction = new OpData(span.Slice(wid, span[wid] >> 16));
            if (instruction.Op == Op.OpShaderSDSL)
                isContext = false;
            (isContext ? context.GetBuffer() : buffer).Add(instruction);
            wid += span[wid] >> 16;
        }

        return new(context, buffer);

View on GitHub (pinned to 96fad776d2)