clockworklabs/SpacetimeDB · error · ArgumentException

Error while reading {typeof(T).FullName}: expected source sp

Error message

Error while reading {typeof(T).FullName}: expected source span to be {expectedSize} bytes long, but was {source.Length} bytes.

What it means

Util.Read<T>(source, littleEndian) deserializes a blittable struct straight from a byte span via MemoryMarshal and therefore requires the span to be exactly Marshal.SizeOf<T>() bytes (16 for ConnectionId, 32 for Identity). A length mismatch means the byte sequence cannot possibly be that struct, so it fails fast with ArgumentException instead of reading garbage.

Source

Thrown at crates/bindings-csharp/BSATN.Runtime/Builtins.cs:61

#endif
    }

    /// <summary>
    /// Convert the passed byte array to a value of type T, optionally reversing it before performing the conversion.
    /// If the input is not reversed, it is treated as having the native endianness of the host system.
    /// (The endianness of the host system can be checked via System.BitConverter.IsLittleEndian.)
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="source"></param>
    /// <param name="littleEndian"></param>
    /// <returns></returns>
    public static T Read<T>(ReadOnlySpan<byte> source, bool littleEndian)
        where T : struct
    {
        var expectedSize = Marshal.SizeOf<T>();
        if (source.Length != expectedSize)
        {
            throw new ArgumentException(
                $"Error while reading {typeof(T).FullName}: expected source span to be {expectedSize} bytes long, but was {source.Length} bytes."
            );
        }

        var result = MemoryMarshal.Read<T>(source);

        if (littleEndian != BitConverter.IsLittleEndian)
        {
            AsBytes(ref result).Reverse();
        }

        return result;
    }

    /// <summary>
    /// Convert a hex string to a byte array.
    /// </summary>
    /// <param name="hex"></param>

View on GitHub (pinned to 524b4487d9)

Solutions

  1. Check bytes.Length == Marshal.SizeOf<T>() (16 / 32 for the ID types) before calling
  2. Prefer the FromHexString entry points, which validate the hex length up front with a clearer message
  3. Fix slicing arithmetic: verify offset + size <= buffer.Length and that you advance by the element size
  4. Log the actual length on failure to pinpoint which producer emits the wrong size

Example fix

// before
var id = ConnectionId.FromBigEndian(Util.StringToByteArray(hex)); // may throw later with generic message

// after
if (hex.Length != 32) throw new ArgumentException($"Expected 32 hex chars, got {hex.Length}");
var id = ConnectionId.FromHexString(hex);
Defensive patterns

Strategy: validation

Validate before calling

static ConnectionId ReadConnectionId(byte[] bytes)
{
    if (bytes.Length != 16)
        throw new ArgumentException($"ConnectionId payload must be 16 bytes, got {bytes.Length}");
    return ConnectionId.FromBigEndian(bytes);
}

Try / catch

try { id = Util.Read<ConnectionId>(span, littleEndian: false); }
catch (ArgumentException ex) { logger.LogError("Bad ID payload: {Message}", ex.Message); throw; }

Prevention

When it happens

Trigger: Calling ConnectionId.FromBigEndian/Identity.FromBigEndian (both delegate to Util.Read) with a byte array that is too short or too long: hex decoded from a wrong-length string, a truncated network/DB blob, or a buffer with extra trailing bytes.

Common situations: Decoding hex strings without length validation; slicing bugs (wrong offset/length) when carving values out of a larger buffer; schema drift where the stored value size changed between versions.

Related errors


AI-assisted analysis of clockworklabs/SpacetimeDB@524b4487d9 (2026-08-16). Data as JSON: /api/errors/ab20c94b5cc4d3b4. Report an issue: GitHub.