CoplayDev/unity-mcp · error · JsonSerializationException
Expected JSON object or null when deserializing Matrix4x4, g
Error message
Expected JSON object or null when deserializing Matrix4x4, got '{reader.TokenType}'. What it means
Thrown by Matrix4x4Converter.ReadJson when the current JSON token is not StartObject and not Null. Matrix4x4 must be deserialized from a JSON object with m00-m33 fields (or null for a zero matrix). Arrays, scalars, or strings are rejected. This differs from the Vector converters which accept arrays.
Source
Thrown at MCPForUnity/Runtime/Serialization/UnityTypeConverters.cs:245
writer.WritePropertyName("m13"); writer.WriteValue(value.m13);
writer.WritePropertyName("m20"); writer.WriteValue(value.m20);
writer.WritePropertyName("m21"); writer.WriteValue(value.m21);
writer.WritePropertyName("m22"); writer.WriteValue(value.m22);
writer.WritePropertyName("m23"); writer.WriteValue(value.m23);
writer.WritePropertyName("m30"); writer.WriteValue(value.m30);
writer.WritePropertyName("m31"); writer.WriteValue(value.m31);
writer.WritePropertyName("m32"); writer.WriteValue(value.m32);
writer.WritePropertyName("m33"); writer.WriteValue(value.m33);
writer.WriteEndObject();
}
public override Matrix4x4 ReadJson(JsonReader reader, Type objectType, Matrix4x4 existingValue, bool hasExistingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
return new Matrix4x4(); // Return zero matrix for null (consistent with missing field defaults)
if (reader.TokenType != JsonToken.StartObject)
throw new JsonSerializationException($"Expected JSON object or null when deserializing Matrix4x4, got '{reader.TokenType}'.");
JObject jo = JObject.Load(reader);
var matrix = new Matrix4x4();
matrix.m00 = jo["m00"]?.Value<float>() ?? 0f;
matrix.m01 = jo["m01"]?.Value<float>() ?? 0f;
matrix.m02 = jo["m02"]?.Value<float>() ?? 0f;
matrix.m03 = jo["m03"]?.Value<float>() ?? 0f;
matrix.m10 = jo["m10"]?.Value<float>() ?? 0f;
matrix.m11 = jo["m11"]?.Value<float>() ?? 0f;
matrix.m12 = jo["m12"]?.Value<float>() ?? 0f;
matrix.m13 = jo["m13"]?.Value<float>() ?? 0f;
matrix.m20 = jo["m20"]?.Value<float>() ?? 0f;
matrix.m21 = jo["m21"]?.Value<float>() ?? 0f;
matrix.m22 = jo["m22"]?.Value<float>() ?? 0f;
matrix.m23 = jo["m23"]?.Value<float>() ?? 0f;
matrix.m30 = jo["m30"]?.Value<float>() ?? 0f;
matrix.m31 = jo["m31"]?.Value<float>() ?? 0f;
matrix.m32 = jo["m32"]?.Value<float>() ?? 0f;View on GitHub (pinned to c21bf496bc)
Solutions
- Send Matrix4x4 as a JSON object with m00-m33 keys: {"m00": 1, "m01": 0, ..., "m33": 1}.
- For an identity matrix, send null (resolves to zero matrix) or build the full object.
- Do NOT use array form for Matrix4x4 (unlike Vector2/3/4 which accept arrays).
- If passing a TRS matrix, serialize it as an object with all 16 named elements.
Example fix
// before
params = {"matrix": [[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]]}
// after
params = {"matrix": {"m00":1,"m01":0,"m02":0,"m03":0,"m10":0,"m11":1,"m12":0,"m13":0,"m20":0,"m21":0,"m22":1,"m23":0,"m30":0,"m31":0,"m32":0,"m33":1}} Defensive patterns
Strategy: type-guard
Validate before calling
def is_valid_matrix4x4(val) -> bool:
# Matrix4x4 MUST be an object with m00-m33, NOT an array
if not isinstance(val, dict):
return False
expected = {f'm{r}{c}' for r in range(4) for c in range(4)}
return expected.issubset(val.keys())
# Identity matrix template:
def identity_matrix():
m = {}
for r in range(4):
for c in range(4):
m[f'm{r}{c}'] = 1.0 if r == c else 0.0
return m Type guard
function isMatrix4x4(val: unknown): val is Record<string, number> | null {
if (val === null) return true;
if (!val || typeof val !== 'object' || Array.isArray(val)) return false;
const keys = ['m00','m01','m02','m03','m10','m11','m12','m13','m20','m21','m22','m23','m30','m31','m32','m33'];
return keys.every(k => k in (val as Record<string, unknown>));
} Try / catch
try
{
var matrix = serializer.Deserialize<Matrix4x4>(reader);
}
catch (JsonSerializationException ex) when (ex.Message.Contains("Expected JSON object or null when deserializing Matrix4x4"))
{
return new ErrorResponse("Matrix4x4 must be a JSON object with m00-m33 keys, not an array.");
} Prevention
- Matrix4x4 uses object form only (unlike Vector converters which accept arrays).
- Include all 16 keys m00-m33 in the object.
- Send null for a zero matrix.
- Don't pass nested arrays for matrices — use the named-element object form.
When it happens
Trigger: Deserializing a Matrix4x4 field from a JSON array (e.g. [[..],[..],..]) or a scalar value. The converter only accepts object form with m00-m33 keys or JSON null. Passing a nested array of 16 floats triggers this.
Common situations: Client sends matrix as a flat or nested array of 16 floats; AI model outputs a 4x4 grid as arrays; confusion with the Vector converters which DO accept arrays; shader TRS matrix sent in array form.
Related errors
- Cannot deserialize Vector3 from {token.Type}: '{token}'
- Cannot deserialize Vector2 from {token.Type}: '{token}'
- Cannot deserialize Quaternion from {token.Type}: '{token}'
- Cannot deserialize Vector4 from {token.Type}: '{token}'
- OpenClaw config contains non-JSON content and cannot be safe
AI-assisted analysis of CoplayDev/unity-mcp@c21bf496bc (2026-08-13).
Data as JSON: /api/errors/adfa8e92f76c418a.
Report an issue: GitHub.