{"record":{"id":"81ca5f00d7e9c2cd","repo":"microsoft/aspire","slug":"a-json-patch-operation-must-contain-string-op-and-path","errorCode":null,"errorMessage":"A JSON Patch operation must contain string 'op' and 'path' properties.","messagePattern":"A JSON Patch operation must contain string 'op' and 'path' properties\\.","errorType":"exception","errorClass":"JsonException","httpStatus":null,"severity":"error","filePath":"src/Aspire.Hosting/Dcp/JsonPatch.cs","lineNumber":37,"sourceCode":"        var operations = new JsonArray();\n        AddOperations(current, changed, string.Empty, operations);\n\n        return operations;\n    }\n\n    internal static JsonNode? Apply(JsonNode? current, JsonArray patch)\n    {\n        var result = current?.DeepClone();\n\n        foreach (var operationNode in patch)\n        {\n            if (operationNode is not JsonObject operation ||\n                operation[\"op\"] is not JsonValue operationValue ||\n                operation[\"path\"] is not JsonValue pathValue ||\n                !operationValue.TryGetValue<string>(out var operationName) ||\n                !pathValue.TryGetValue<string>(out var path))\n            {\n                throw new JsonException(\"A JSON Patch operation must contain string 'op' and 'path' properties.\");\n            }\n\n            var segments = ParsePath(path);\n            var hasValue = operation.TryGetPropertyValue(\"value\", out var value);\n            result = ApplyOperation(result, operationName, segments, hasValue, value);\n        }\n\n        return result;\n    }\n\n    private static void AddOperations(JsonNode? current, JsonNode? changed, string path, JsonArray operations)\n    {\n        if (JsonNode.DeepEquals(current, changed))\n        {\n            return;\n        }\n\n        if (current is JsonObject currentObject && changed is JsonObject changedObject)","sourceCodeStart":19,"sourceCodeEnd":55,"githubUrl":"https://github.com/microsoft/aspire/blob/25830f84bd145686607ad00c057b3f84e2e51d43/src/Aspire.Hosting/Dcp/JsonPatch.cs#L19-L55","documentation":"JsonPatch.Apply parses each operation of a JSON Patch document and requires every operation to be a JSON object with string-valued 'op' and 'path' properties per RFC 6902. If an operation is not an object, or 'op'/'path' are missing or not strings, a JsonException is thrown to abort application of the patch.","triggerScenarios":"Applying a patch document whose operation lacks 'op' or 'path', or where these are non-string (e.g. \"op\": 1, or a shorthand form); patch JSON deserialized from an untrusted/incorrect source via ApplyUpdate on a DCP resource snapshot.","commonSituations":"Hand-written patch JSON with typos ('operation' instead of 'op'); producers emitting numeric op codes; schema drift between the patch producer and Aspire.Hosting's parser.","solutions":["Ensure every patch operation is a JSON object containing both \"op\" (string) and \"path\" (string).","Validate the patch document against RFC 6902 before applying it.","Check the producer of the patch (custom resource update code) for typos in property names.","Log the raw patch JSON to identify which operation index is malformed."],"exampleFix":"// before\n[ { \"operation\": \"replace\", \"path\": \"/spec/replicas\", \"value\": 3 } ]\n// after\n[ { \"op\": \"replace\", \"path\": \"/spec/replicas\", \"value\": 3 } ]","handlingStrategy":"validation","validationCode":"bool IsValidOperation(JsonNode node) =>\n    node is JsonObject o &&\n    o[\"op\"] is JsonValue ov && ov.TryGetValue<string>(out _) &&\n    o[\"path\"] is JsonValue pv && pv.TryGetValue<string>(out _);","typeGuard":"bool IsStringProperty(JsonObject o, string name) => o[name] is JsonValue v && v.TryGetValue<string>(out _);","tryCatchPattern":"try { patch.Apply(target); }\ncatch (JsonException ex)\n{\n    logger.LogError(ex, \"Malformed JSON Patch document: {Message}\", ex.Message);\n}","preventionTips":["Validate the patch document against RFC 6902 before applying.","Always emit string 'op' and 'path' in every operation.","Log the raw patch JSON when application fails to spot malformed ops."],"tags":["json-patch","rfc6902","schema-validation","dcp"],"backgroundTag":"schema-validation-failed","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"}