Devolutions/UniGetUI · error · InvalidDataException

JsonNode? pkg was null, when it shouldn't

Error message

JsonNode? pkg was null, when it shouldn't

What it means

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.

Source

Thrown at src/UniGetUI.PackageEngine.Serializable/SerializableBundle.cs:52

                export_version = this.export_version,
                packages = _packages,
                incompatible_packages_info = this.incompatible_packages_info,
                incompatible_packages = _incompatPackages,
            };
        }

        public override void LoadFromJson(JsonNode data)
        {
            this.export_version = data[nameof(export_version)]?.GetVal<double>() ?? 0;
            this.incompatible_packages_info =
                data[nameof(incompatible_packages_info)]?.GetVal<string>() ?? IncompatMessage;
            this.packages = new List<SerializablePackage>();
            this.incompatible_packages = new List<SerializableIncompatiblePackage>();

            foreach (JsonNode? pkg in data[nameof(packages)]?.AsArray2() ?? new())
            {
                if (pkg is null)
                    throw new InvalidDataException("JsonNode? pkg was null, when it shouldn't");
                packages.Add(new SerializablePackage(pkg));
            }

            foreach (JsonNode? inc_pkg in data[nameof(incompatible_packages)]?.AsArray2() ?? new())
            {
                if (inc_pkg is null)
                    throw new InvalidDataException("JsonNode? inc_pkg was null, when it shouldn't");
                incompatible_packages.Add(new SerializableIncompatiblePackage(inc_pkg));
            }
        }

        public override JsonObject AsJsonNode()
        {
            JsonObject obj = new();
            obj.Add(nameof(export_version), export_version);
            obj.Add(
                nameof(packages),
                new JsonArray(packages.Select(p => p.AsJsonNode()).ToArray())

View on GitHub (pinned to 9b1d7d0eab)

Solutions

  1. Regenerate the bundle export from a working UniGetUI instance and re-import.
  2. Pre-clean the file: remove null elements from the 'packages' (and 'incompatible_packages') arrays before import.
  3. Verify export_version equals SerializableBundle.ExpectedVersion (3); an older/incompatible format is a frequent root cause of malformed arrays.
  4. 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.
  5. Diff the failing file against a known-good export to find which element is null.

Example fix

// before (SerializableBundle.cs:49-54)
foreach (JsonNode? pkg in data[nameof(packages)]?.AsArray2() ?? new())
{
    if (pkg is null)
        throw new InvalidDataException("JsonNode? pkg was null, when it shouldn't");
    packages.Add(new SerializablePackage(pkg));
}

// after — skip corrupt null slots instead of aborting the entire import
foreach (JsonNode? pkg in data[nameof(packages)]?.AsArray2() ?? new())
{
    if (pkg is null) continue;
    packages.Add(new SerializablePackage(pkg));
}
Defensive patterns

Strategy: validation

Validate before calling

// Strip null elements from the 'packages' array before deserializing.
static JsonNode SanitizeBundle(JsonNode node)
{
    if (node is JsonObject obj && obj["packages"] is JsonArray pkgs)
    {
        JsonArray clean = new();
        foreach (var p in pkgs.Where(n => n is not null))
            clean.Add(p.DeepClone());
        obj["packages"] = clean;
    }
    if (node is JsonObject obj2 && obj2["export_version"]?.GetVal<double>() != SerializableBundle.ExpectedVersion)
        throw new InvalidDataException($"Unexpected bundle export_version");
    return node;
}

var safe = SanitizeBundle(JsonNode.Parse(File.ReadAllText(path))!);
var bundle = new SerializableBundle(safe);

Type guard

static bool BundlePackagesHaveNoNulls(JsonNode node)
{
    if (node is not JsonObject obj) return false;
    if (obj["packages"] is not JsonArray arr) return true; // absent array is fine
    return arr.All(e => e is not null);
}

Try / catch

try
{
    var bundle = new SerializableBundle(JsonNode.Parse(json)!);
}
catch (InvalidDataException ex) when (ex.Message.Contains("pkg was null"))
{
    Logger.Error("Bundle file has null package entries; refusing to import corrupt file.");
    // re-export instead of importing partial data
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of Devolutions/UniGetUI@9b1d7d0eab (2026-08-13). Data as JSON: /api/errors/0412fa68838d8887. Report an issue: GitHub.