{"record":{"id":"6ea70f3dfd9a382e","repo":"clockworklabs/SpacetimeDB","slug":"result-failed-without-an-error-object","errorCode":null,"errorMessage":"Result failed without an error object.","messagePattern":"Result failed without an error object\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"crates/bindings-csharp/BSATN.Runtime/Builtins.cs","lineNumber":659,"sourceCode":"    public TResult Match<TResult>(Func<T, TResult> onOk, Func<E, TResult> onErr) =>\n        this switch\n        {\n            OkR(var v) => onOk(v),\n            ErrR(var e) => onErr(e),\n            _ => throw new InvalidOperationException(\"Unknown Result variant.\"),\n        };\n\n    public static Result<T, E> Ok(T value) => new OkR(value);\n\n    public static Result<T, E> Err(E error) => new ErrR(error);\n\n    public T UnwrapOrThrow()\n    {\n        return this switch\n        {\n            OkR(var v) => v,\n            ErrR(var e) when e is not null => throw new Exception(e.ToString()),\n            ErrR(_) => throw new InvalidOperationException(\n                \"Result failed without an error object.\"\n            ),\n            _ => throw new InvalidOperationException(\"Unknown Result variant.\"),\n        };\n    }\n\n    public T UnwrapOr(T defaultValue) =>\n        this switch\n        {\n            OkR(var v) => v,\n            _ => defaultValue,\n        };\n\n    public T UnwrapOrElse(Func<E, T> f) =>\n        this switch\n        {\n            OkR(var v) => v,\n            ErrR(var e) => f(e),","sourceCodeStart":641,"sourceCodeEnd":677,"githubUrl":"https://github.com/clockworklabs/SpacetimeDB/blob/6dee26c6efc2856793e12b148a59742964f5d783/crates/bindings-csharp/BSATN.Runtime/Builtins.cs#L641-L677","documentation":"SpacetimeDB's Result<T,E> (a TaggedEnum with OkR/ErrR variants) throws this from UnwrapOrThrow() when the value IS an ErrR but its Error payload is null, so there is no exception object to surface. The non-null Err path rethrows the stored error via `throw new Exception(e.ToString())`; the null-Err path falls through to this InvalidOperationException. In practice it means code produced Result.Err(default) or Err(null) and then called UnwrapOrThrow().","triggerScenarios":"Calling UnwrapOrThrow() on a Result<T,E> built as Result<T,E>.Err(null) or Err(default(E)) where E is a nullable/reference type; or deserializing a Result whose error payload decoded to null (e.g. ErrR over a nullable string error read from malformed BSATN bytes); or a TryWithTx-style callback returning Err(null) (see TransactionalContextState.WithTx which pipes body() results into UnwrapOrThrow).","commonSituations":"Module code returning Result.Ok(value)/Err(error) with a default-constructed error struct; generic helper methods that do `return Result<T,E>.Err(error!)` without a null check; deserialization of wire Results where the error type is an option/string that decoded empty; upgrading code where E changed from value type to reference type so default(E) became null.","solutions":["Audit every Result<T,E>.Err(...) construction on the failing path and pass a non-null error value (e.g. Err(\"reason\") or Err(new MyError(...)))","If E is nullable, prefer UnwrapOr(defaultValue) or UnwrapOrElse(_ => ...) instead of UnwrapOrThrow()","When the Result comes from the wire, inspect the raw error payload/bytes before unwrapping to confirm the error type matches the schema","If the Err is produced by library internals (e.g. TryWithTx), report it as a SpacetimeDB bindings bug with a repro"],"exampleFix":"// before\nvar result = Result<int, string>.Err(null!);\nreturn result.UnwrapOrThrow(); // throws \"Result failed without an error object.\"\n\n// after - always store a real error, or unwrap without throwing\nvar result = Result<int, string>.Err(\"value out of range\");\nreturn result.UnwrapOrThrow(); // throws Exception(\"value out of range\")\n// or: int v = result.UnwrapOr(-1);","handlingStrategy":"validation","validationCode":"// Before UnwrapOrThrow, verify an Err actually carries an error object:\nif (result is Result<int, string>.ErrR { Error: null })\n{\n    // empty Err: don't unwrap, handle it here (log, default, or re-Err with a real message)\n}","typeGuard":"static bool HasThrowableError<T, E>(Result<T, E> r)\n    where E : class =>\n    r is not (Result<T, E>.ErrR { Error: null });","tryCatchPattern":"try { var v = result.UnwrapOrThrow(); }\ncatch (InvalidOperationException ioe) when (ioe.Message.Contains(\"without an error object\"))\n{\n    // the Err was constructed with a null error; fix the producer of this Result\n}","preventionTips":["Never pass null/default to Result.Err — always a descriptive error value","Prefer UnwrapOr/UnwrapOrElse at API boundaries where a null error is possible","Enable nullable reference types so Err(null) on reference-typed E is a warning at the call site"],"tags":["csharp","bsatn","result","null-check","spacetedb-module"],"backgroundTag":"result-unwrap-null-error","analyzedSha":"6dee26c6efc2856793e12b148a59742964f5d783","analyzedAt":"2026-08-20T06:08:37.179Z","contentChangedAt":"2026-08-20T06:08:37.179Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}