microsoft/aspire · error · JsonException

A JSON Patch path can only traverse objects and arrays.

Error message

A JSON Patch path can only traverse objects and arrays.

What it means

JsonPatch.Apply resolves each segment of a JSON Patch path through the document tree. When an intermediate node is neither a JsonObject nor a JsonArray (e.g. a scalar like a string or number sits mid-path), the library cannot descend further and throws this JsonException. It enforces RFC 6902 semantics that a path must only traverse containers.

Solutions

  1. Inspect the target document at the failing path and correct the patch path so it only traverses objects/arrays
  2. Verify the resource's current JSON shape (e.g. via Dashboard or kubectl-style dump) before composing the path
  3. Split the patch into operations that create intermediate objects first (add) before descending

Example fix

// before
patch = new("replace", "/env/NAME/value", value); // 'NAME' is a string, not an object
// after
patch = new("add", "/env", new JsonObject { ["NAME"] = value });
Defensive patterns

Strategy: try-catch

Validate before calling

static bool IsPathTraversable(JsonNode root, string path) {
    var current = root;
    foreach (var seg in path.Split('/', StringSplitOptions.RemoveEmptyEntries)) {
        if (current is JsonObject o) { if (!o.TryGetPropertyValue(seg, out current) || current is null) return false; }
        else if (current is JsonArray a) { if (!int.TryParse(seg, out var i) || i < 0 || i >= a.Count) return false; current = a[i]; }
        else return false; // scalar mid-path
    }
    return true;
}

Type guard

bool IsContainer(JsonNode? node) => node is JsonObject or JsonArray;

Try / catch

try { patcher.Apply(doc, operations); }
catch (JsonException ex) when (ex.Message.Contains("can only traverse")) {
    logger.LogWarning(ex, "Patch path descends into a scalar node; fix the path");
}

Prevention

When it happens

Trigger: Calling Apply with a patch operation whose path points through a JSON property that holds a scalar, e.g. path '/env/VALUE/more' where 'VALUE' is a string.

Common situations: Hand-written patch paths that assume deeper nesting than the DCP resource actually has; a resource schema version change that flattened a section; passing a segment that collides with a scalar property name.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of microsoft/aspire@25830f84bd (2026-09-16). Data as JSON: /api/errors/1f846c778b0babb1. Report an issue: GitHub.

Appendix: source

Thrown at src/Aspire.Hosting/Dcp/JsonPatch.cs:212

            {
                "remove" => null,
                _ => value?.DeepClone(),
            };
        }

        var parent = GetParent(current, segments);
        var finalSegment = segments[^1];

        switch (parent)
        {
            case JsonObject jsonObject:
                ApplyToObject(jsonObject, operation, finalSegment, value);
                break;
            case JsonArray jsonArray:
                ApplyToArray(jsonArray, operation, finalSegment, value);
                break;
            default:
                throw new JsonException("A JSON Patch path can only traverse objects and arrays.");
        }

        return current;
    }

    private static JsonNode GetParent(JsonNode? current, string[] segments)
    {
        var parent = current ?? throw new JsonException("A JSON Patch path cannot traverse a null value.");

        for (var index = 0; index < segments.Length - 1; index++)
        {
            var segment = segments[index];
            parent = parent switch
            {
                JsonObject jsonObject when jsonObject.TryGetPropertyValue(segment, out var child) && child is not null => child,
                JsonArray jsonArray => jsonArray[ParseArrayIndex(segment, jsonArray.Count, allowEnd: false)]
                    ?? throw new JsonException($"JSON Patch path segment '{segment}' refers to a null value."),
                _ => throw new JsonException($"JSON Patch path segment '{segment}' does not exist."),

View on GitHub (pinned to 25830f84bd)