{"record":{"id":"5d58ed57688a1f0b","repo":"microsoft/aspire","slug":"failed-to-get-or-create-property-key","errorCode":null,"errorMessage":"Failed to get or create property '{key}'","messagePattern":"Failed to get or create property '(.+?)'","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/Aspire.Hosting.Azure/Provisioning/JsonExtensions.cs","lineNumber":29,"sourceCode":"    {\n        var jsonObj = obj.AsObject();\n\n        // Try to get the existing node\n        var node = jsonObj[key];\n        if (node is not null)\n        {\n            return node;\n        }\n\n        // Create a new node and try to add it\n        node = new JsonObject();\n\n        if (!jsonObj.TryAdd(key, node))\n        {\n            node = jsonObj[key];\n            if (node is null)\n            {\n                throw new InvalidOperationException($\"Failed to get or create property '{key}'\");\n            }\n        }\n\n        return node;\n    }\n}\n","sourceCodeStart":11,"sourceCodeEnd":36,"githubUrl":"https://github.com/microsoft/aspire/blob/25830f84bd145686607ad00c057b3f84e2e51d43/src/Aspire.Hosting.Azure/Provisioning/JsonExtensions.cs#L11-L36","documentation":"This internal JsonExtensions.Prop helper guarantees that indexing a JsonObject by key always returns a non-null JsonNode: it reads the property, and if missing, adds a new empty JsonObject under that key. The InvalidOperationException is thrown only if TryAdd fails (the key was concurrently added or the node exists with a JSON null value) and the subsequent lookup still yields null. In practice this fires when the property exists but holds a JSON literal null rather than an object, so TryAdd cannot replace it and the stored null is returned.","triggerScenarios":"Calling Prop() on a JSON property whose stored value is JSON null: jsonObj[key] is not null-check-friendly here only when TryAdd fails — i.e. the key exists with a null JsonNode — so the fallback jsonObj[key] returns null and the exception fires. Also possible with concurrent mutation of the same JsonObject from multiple threads between the read and TryAdd.","commonSituations":"Parsing Bicep/ARM parameter files or deployment outputs where a nested property is explicitly set to null (\"parameters\": { \"foo\": null }); code traversing that shape with obj.Prop(\"foo\").Prop(\"bar\") hits the null node. Concurrent JSON mutation from parallel tasks sharing one JsonObject.","solutions":["Inspect the JSON document being navigated: replace any explicit JSON null for the failing key with an object ({}), or remove the property.","Guard traversal with jsonObj.ContainsKey/key checks or [key] is JsonObject checks before calling Prop for keys that may be null.","Avoid sharing a mutable JsonObject across concurrent tasks; build JSON trees single-threaded or under a lock.","If this appears without an obvious null in the input, treat it as an Aspire internal invariant break and report it with the JSON payload being processed."],"exampleFix":"// before\nvar value = bicepJson.Prop(\"parameters\").Prop(\"location\");\n\n// after\nif (bicepJson[\"parameters\"] is not JsonObject paramsObj)\n{\n    throw new InvalidOperationException(\"'parameters' is missing or not an object (check for JSON null).\");\n}\nvar value = paramsObj.Prop(\"location\");","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"static bool TryProp(JsonNode? obj, string key, out JsonNode? node)\n{\n    node = (obj as JsonObject)?[key];\n    return node is JsonObject;   // false when the property is JSON null or missing\n}","tryCatchPattern":"try\n{\n    var value = json.Prop(\"parameters\").Prop(key);\n}\ncatch (InvalidOperationException ex) when (ex.Message.StartsWith(\"Failed to get or create property\"))\n{\n    logger.LogWarning(ex, \"JSON property was null or unexpectedly absent; skipping.\");\n}","preventionTips":["Never set JSON properties to explicit null in payloads that will be traversed with Prop().","Use [key] is JsonObject checks before deep navigation of parsed JSON.","Keep JsonObject mutation single-threaded or synchronized; don't share mutable JSON trees across tasks.","Treat any occurrence of this exception as a bug report against Aspire — the helper is designed never to return null."],"tags":["json","internal","parsing","aspire"],"backgroundTag":"json-parse-error","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"}