{"record":{"id":"1f846c778b0babb1","repo":"microsoft/aspire","slug":"a-json-patch-path-can-only-traverse-objects-and-arrays","errorCode":null,"errorMessage":"A JSON Patch path can only traverse objects and arrays.","messagePattern":"A JSON Patch path can only traverse objects and arrays\\.","errorType":"exception","errorClass":"JsonException","httpStatus":null,"severity":"error","filePath":"src/Aspire.Hosting/Dcp/JsonPatch.cs","lineNumber":212,"sourceCode":"            {\n                \"remove\" => null,\n                _ => value?.DeepClone(),\n            };\n        }\n\n        var parent = GetParent(current, segments);\n        var finalSegment = segments[^1];\n\n        switch (parent)\n        {\n            case JsonObject jsonObject:\n                ApplyToObject(jsonObject, operation, finalSegment, value);\n                break;\n            case JsonArray jsonArray:\n                ApplyToArray(jsonArray, operation, finalSegment, value);\n                break;\n            default:\n                throw new JsonException(\"A JSON Patch path can only traverse objects and arrays.\");\n        }\n\n        return current;\n    }\n\n    private static JsonNode GetParent(JsonNode? current, string[] segments)\n    {\n        var parent = current ?? throw new JsonException(\"A JSON Patch path cannot traverse a null value.\");\n\n        for (var index = 0; index < segments.Length - 1; index++)\n        {\n            var segment = segments[index];\n            parent = parent switch\n            {\n                JsonObject jsonObject when jsonObject.TryGetPropertyValue(segment, out var child) && child is not null => child,\n                JsonArray jsonArray => jsonArray[ParseArrayIndex(segment, jsonArray.Count, allowEnd: false)]\n                    ?? throw new JsonException($\"JSON Patch path segment '{segment}' refers to a null value.\"),\n                _ => throw new JsonException($\"JSON Patch path segment '{segment}' does not exist.\"),","sourceCodeStart":194,"sourceCodeEnd":230,"githubUrl":"https://github.com/microsoft/aspire/blob/25830f84bd145686607ad00c057b3f84e2e51d43/src/Aspire.Hosting/Dcp/JsonPatch.cs#L194-L230","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Inspect the target document at the failing path and correct the patch path so it only traverses objects/arrays","Verify the resource's current JSON shape (e.g. via Dashboard or kubectl-style dump) before composing the path","Split the patch into operations that create intermediate objects first (add) before descending"],"exampleFix":"// before\npatch = new(\"replace\", \"/env/NAME/value\", value); // 'NAME' is a string, not an object\n// after\npatch = new(\"add\", \"/env\", new JsonObject { [\"NAME\"] = value });","handlingStrategy":"try-catch","validationCode":"static bool IsPathTraversable(JsonNode root, string path) {\n    var current = root;\n    foreach (var seg in path.Split('/', StringSplitOptions.RemoveEmptyEntries)) {\n        if (current is JsonObject o) { if (!o.TryGetPropertyValue(seg, out current) || current is null) return false; }\n        else if (current is JsonArray a) { if (!int.TryParse(seg, out var i) || i < 0 || i >= a.Count) return false; current = a[i]; }\n        else return false; // scalar mid-path\n    }\n    return true;\n}","typeGuard":"bool IsContainer(JsonNode? node) => node is JsonObject or JsonArray;","tryCatchPattern":"try { patcher.Apply(doc, operations); }\ncatch (JsonException ex) when (ex.Message.Contains(\"can only traverse\")) {\n    logger.LogWarning(ex, \"Patch path descends into a scalar node; fix the path\");\n}","preventionTips":["Inspect the live resource JSON before composing deep patch paths","Keep patch paths shallow; create intermediate objects with 'add' first","Add unit tests that run patches against representative resource documents"],"tags":["json-patch","path-traversal","json"],"backgroundTag":"invalid-argument-value","analyzedSha":"25830f84bd145686607ad00c057b3f84e2e51d43","analyzedAt":"2026-09-16T11:10:06.193Z","contentChangedAt":"2026-09-16T11:10:06.193Z","schemaVersion":2},"datasetVersion":"2026-09-21T04:17:39.646Z"}