{"record":{"id":"d33cd9cc2ea660fe","repo":"reactiveui/refit","slug":"problem-details-json-must-be-an-object","errorCode":null,"errorMessage":"Problem details JSON must be an object.","messagePattern":"Problem details JSON must be an object\\.","errorType":"exception","errorClass":"JsonException","httpStatus":null,"severity":"error","filePath":"src/Refit/ValidationApiException.cs","lineNumber":144,"sourceCode":"\n        return new(exception);\n    }\n\n    /// <summary>Deserializes RFC 7807 problem details without requiring public setters on extension data.</summary>\n    /// <param name=\"content\">The JSON problem details content.</param>\n    /// <returns>The deserialized problem details.</returns>\n    /// <exception cref=\"JsonException\"><paramref name=\"content\"/> is not valid JSON, or its root value is not an object.</exception>\n    internal static ProblemDetails DeserializeProblemDetails(string content)\n    {\n#if NET10_0_OR_GREATER\n        var rootElement = JsonElement.Parse(content);\n#else\n        using var document = JsonDocument.Parse(content);\n        var rootElement = document.RootElement;\n#endif\n        if (rootElement.ValueKind != JsonValueKind.Object)\n        {\n            throw new JsonException(\"Problem details JSON must be an object.\");\n        }\n\n        var problemDetails = new ProblemDetails();\n        foreach (var property in rootElement.EnumerateObject())\n        {\n            ReadProblemDetailsProperty(problemDetails, property);\n        }\n\n        return problemDetails;\n    }\n\n    /// <summary>Reads a single problem-details property.</summary>\n    /// <param name=\"problemDetails\">The problem details instance to populate.</param>\n    /// <param name=\"property\">The JSON property to read.</param>\n    internal static void ReadProblemDetailsProperty(\n        ProblemDetails problemDetails,\n        JsonProperty property)\n    {","sourceCodeStart":126,"sourceCodeEnd":162,"githubUrl":"https://github.com/reactiveui/refit/blob/b455f65ecc4c97d092317e349cb775f9cfc6bcdf/src/Refit/ValidationApiException.cs#L126-L162","documentation":"DeserializeProblemDetails throws JsonException when the parsed problem-details content's root JSON value is not an object (e.g. it is an array, a bare string/number, true/false, or null). RFC 7807 problem details must be a JSON object, so any other root kind is rejected after parsing succeeds.","triggerScenarios":"The response body parses as valid JSON but its top-level value is not an object: a JSON array, a scalar, or a JSON null. Reached via ValidationApiException deserialization when the server returned a structurally valid but non-object payload claiming to be problem+json.","commonSituations":"A server returning a JSON array of errors instead of a single problem-details object; a proxy returning a scalar status; a misconfigured error formatter; version skew where the API changed its error envelope shape.","solutions":["Ensure the server returns a single RFC 7807 problem-details JSON object as the response body","If the API legitimately returns arrays/scalars, deserialize with a custom handler instead of ValidationApiException","Inspect the raw content and content type before attempting problem-details deserialization","Wrap deserialization in a try/catch for JsonException and fall back to ApiException"],"exampleFix":"// before\nvar validation = ex.AsValidation(); // throws if body is e.g. [ { ... }, { ... } ]\n\n// after\ntry\n{\n    var validation = ex.AsValidation();\n}\ncatch (JsonException)\n{\n    // body is valid JSON but not a problem-details object; handle generically\n    logger.LogWarning(\"Non-object problem body: {Body}\", ex.Content);\n    throw;\n}","handlingStrategy":"try-catch","validationCode":"static bool IsJsonObjectPayload(string content)\n{\n    using var doc = JsonDocument.Parse(content);\n    return doc.RootElement.ValueKind == JsonValueKind.Object;\n}\n\nif (!string.IsNullOrWhiteSpace(ex.Content) && IsJsonObjectPayload(ex.Content))\n    var validation = ex.AsValidation();","typeGuard":"static bool IsProblemDetailsObject(string content)\n{\n    try\n    {\n        using var doc = JsonDocument.Parse(content);\n        return doc.RootElement.ValueKind == JsonValueKind.Object;\n    }\n    catch { return false; }\n}","tryCatchPattern":"try { var validation = ex.AsValidation(); }\ncatch (JsonException ex) when (ex.Message.Contains(\"must be an object\"))\n{\n    // body is valid JSON but not an object; handle generically as ApiException\n    logger.LogWarning(\"Non-object problem body: {Body}\", ex2.Content);\n}","preventionTips":["Verify the response body is a JSON object before problem-details deserialization","Wrap AsValidation() in a try/catch for JsonException and fall back to ApiException","Log raw content when deserialization fails to diagnose server-side shape changes","Add contract tests against the server's actual error envelope shape"],"tags":["serialization","rfc7807","problem-details","json","jsonexception"],"backgroundTag":null,"analyzedSha":"b455f65ecc4c97d092317e349cb775f9cfc6bcdf","analyzedAt":"2026-08-13T21:20:57.878Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}