{"record":{"id":"0412fa68838d8887","repo":"Devolutions/UniGetUI","slug":"jsonnode-pkg-was-null-when-it-shouldn-t","errorCode":null,"errorMessage":"JsonNode? pkg was null, when it shouldn't","messagePattern":"JsonNode\\? pkg was null, when it shouldn't","errorType":"exception","errorClass":"InvalidDataException","httpStatus":null,"severity":"error","filePath":"src/UniGetUI.PackageEngine.Serializable/SerializableBundle.cs","lineNumber":52,"sourceCode":"                export_version = this.export_version,\n                packages = _packages,\n                incompatible_packages_info = this.incompatible_packages_info,\n                incompatible_packages = _incompatPackages,\n            };\n        }\n\n        public override void LoadFromJson(JsonNode data)\n        {\n            this.export_version = data[nameof(export_version)]?.GetVal<double>() ?? 0;\n            this.incompatible_packages_info =\n                data[nameof(incompatible_packages_info)]?.GetVal<string>() ?? IncompatMessage;\n            this.packages = new List<SerializablePackage>();\n            this.incompatible_packages = new List<SerializableIncompatiblePackage>();\n\n            foreach (JsonNode? pkg in data[nameof(packages)]?.AsArray2() ?? new())\n            {\n                if (pkg is null)\n                    throw new InvalidDataException(\"JsonNode? pkg was null, when it shouldn't\");\n                packages.Add(new SerializablePackage(pkg));\n            }\n\n            foreach (JsonNode? inc_pkg in data[nameof(incompatible_packages)]?.AsArray2() ?? new())\n            {\n                if (inc_pkg is null)\n                    throw new InvalidDataException(\"JsonNode? inc_pkg was null, when it shouldn't\");\n                incompatible_packages.Add(new SerializableIncompatiblePackage(inc_pkg));\n            }\n        }\n\n        public override JsonObject AsJsonNode()\n        {\n            JsonObject obj = new();\n            obj.Add(nameof(export_version), export_version);\n            obj.Add(\n                nameof(packages),\n                new JsonArray(packages.Select(p => p.AsJsonNode()).ToArray())","sourceCodeStart":34,"sourceCodeEnd":70,"githubUrl":"https://github.com/Devolutions/UniGetUI/blob/9b1d7d0eab91620fc15c86b36676532095936ae3/src/UniGetUI.PackageEngine.Serializable/SerializableBundle.cs#L34-L70","documentation":"SerializableBundle.LoadFromJson deserializes an exported UniGetUI bundle (export_version, expected 3). It reads the 'packages' JSON array via AsArray2 and iterates JsonNode? elements. System.Text.Json yields a null JsonNode for every JSON `null` literal element; the loop treats any null as corrupt data and throws InvalidDataException. Unlike the listing timeouts, this throw is NOT caught here — it propagates to the caller doing the bundle import.","triggerScenarios":"Constructing `new SerializableBundle(node)` or calling LoadFromJson(node) where node[\"packages\"] is a JSON array containing at least one null element, e.g. `{\"export_version\":3,\"packages\":[null,{\"Id\":\"x\"}]}`. AsArray2 only reshapes scalar/empty-object nodes into arrays; it does not strip null array elements.","commonSituations":"A bundle export file truncated by a crash mid-write; hand-editing the export JSON and leaving a trailing comma or empty slot that serializes as null; importing a bundle produced by a different/older UniGetUI version whose exporter emitted sparse arrays; concatenating export fragments leaving null placeholders.","solutions":["Regenerate the bundle export from a working UniGetUI instance and re-import.","Pre-clean the file: remove null elements from the 'packages' (and 'incompatible_packages') arrays before import.","Verify export_version equals SerializableBundle.ExpectedVersion (3); an older/incompatible format is a frequent root cause of malformed arrays.","If null slots are acceptable gaps in your workflow, relax the throw to `if (pkg is null) continue;` so corrupt slots are skipped instead of aborting the whole import.","Diff the failing file against a known-good export to find which element is null."],"exampleFix":"// before (SerializableBundle.cs:49-54)\nforeach (JsonNode? pkg in data[nameof(packages)]?.AsArray2() ?? new())\n{\n    if (pkg is null)\n        throw new InvalidDataException(\"JsonNode? pkg was null, when it shouldn't\");\n    packages.Add(new SerializablePackage(pkg));\n}\n\n// after — skip corrupt null slots instead of aborting the entire import\nforeach (JsonNode? pkg in data[nameof(packages)]?.AsArray2() ?? new())\n{\n    if (pkg is null) continue;\n    packages.Add(new SerializablePackage(pkg));\n}","handlingStrategy":"validation","validationCode":"// Strip null elements from the 'packages' array before deserializing.\nstatic JsonNode SanitizeBundle(JsonNode node)\n{\n    if (node is JsonObject obj && obj[\"packages\"] is JsonArray pkgs)\n    {\n        JsonArray clean = new();\n        foreach (var p in pkgs.Where(n => n is not null))\n            clean.Add(p.DeepClone());\n        obj[\"packages\"] = clean;\n    }\n    if (node is JsonObject obj2 && obj2[\"export_version\"]?.GetVal<double>() != SerializableBundle.ExpectedVersion)\n        throw new InvalidDataException($\"Unexpected bundle export_version\");\n    return node;\n}\n\nvar safe = SanitizeBundle(JsonNode.Parse(File.ReadAllText(path))!);\nvar bundle = new SerializableBundle(safe);","typeGuard":"static bool BundlePackagesHaveNoNulls(JsonNode node)\n{\n    if (node is not JsonObject obj) return false;\n    if (obj[\"packages\"] is not JsonArray arr) return true; // absent array is fine\n    return arr.All(e => e is not null);\n}","tryCatchPattern":"try\n{\n    var bundle = new SerializableBundle(JsonNode.Parse(json)!);\n}\ncatch (InvalidDataException ex) when (ex.Message.Contains(\"pkg was null\"))\n{\n    Logger.Error(\"Bundle file has null package entries; refusing to import corrupt file.\");\n    // re-export instead of importing partial data\n}","preventionTips":["Always import bundles produced by the same UniGetUI major version (export_version == 3).","Validate the file is complete (no truncation) before import — check it ends with a closing brace and parses cleanly.","Do not hand-edit exported bundle JSON; regenerate it instead.","Add a schema/null-element check in any tooling that writes bundles so null slots never reach LoadFromJson."],"tags":["json","deserialization","data-validation","import"],"backgroundTag":null,"analyzedSha":"9b1d7d0eab91620fc15c86b36676532095936ae3","analyzedAt":"2026-08-13T12:19:18.278Z","schemaVersion":2},"datasetVersion":"2026-08-13T14:17:21.547Z"}