Check the failed action before editing its schema
Open the failed run and compare the Content sent to Parse JSON with its schema. Note where the error occurs and which type was expected and received. An error in the whole input is different from one inside the third array item. Remove or protect personal data before sharing the input.
Build a manually triggered test flow using only Compose and Parse JSON. For each example, paste the shown json(...) expression into Compose named Payload. Add Parse JSON named Parse_JSON, set Content to outputs('Payload'), and paste the shown schema. Add a final Compose to inspect the result. Names must match your actual action names.
These are cloud-flow actions, not the Power Fx ParseJSON function in a Canvas app. Microsoft’s Parse JSON action documentation explains how to supply Content and Schema or generate a starting schema from a sample. One successful sample does not cover every kind of input you may receive.
Example 1: validate one object
A request for one work item is an object with two required strings. Use this Payload expression:
json('{"WorkId":"WK-0042","Title":"Beacon kickoff"}')Schema:
{
"type": "object",
"properties": {
"WorkId": {
"type": "string"
},
"Title": {
"type": "string"
}
},
"required": [
"WorkId",
"Title"
]
}Final Compose expression:
body('Parse_JSON')?['Title']Expected output: Beacon kickoff. Parse JSON checks the data and makes its properties available. It does not rename Title, trim its value or flatten the object.
Edge tests: remove Title and the required-property check should fail. Set Title to null and the string-type check should fail. Set Title to "" and this schema passes: an empty string is still a string. Add a separate check if the title must contain usable text. JSON Schema documents properties and required fields.
Example 2: validate an array without losing rows
The same source now returns two work items:
json('[{"WorkId":"WK-0042","Title":"Beacon kickoff"},{"WorkId":"WK-0043","Title":"Harbor review"}]')The root schema must describe an array. Its items schema describes each member:
{
"type": "array",
"items": {
"type": "object",
"properties": {
"WorkId": {
"type": "string"
},
"Title": {
"type": "string"
}
},
"required": [
"WorkId",
"Title"
]
}
}Use length(body('Parse_JSON')) in the final Compose. Expected output: 2. To process all rows, use the parsed body as Apply to each input, then item()?['WorkId'] inside that loop.
Edge tests: [] is a valid empty array here and has length 0. A one-item array is still an array. An object root fails this schema. A member missing WorkId fails even when other members are valid. JSON Schema’s array reference explains how items applies to array members.
Do not use first() just to make an object schema pass; that discards the remaining records. If a business operation requires exactly one match, check how many matches there are and decide what to do with zero or multiple matches.
Example 3: separate optional from nullable
A work item always has WorkId, but may not have an assigned Owner. Its optional Note must be text when provided. Use this input and schema:
json('{"WorkId":"WK-0042","Owner":null}'){
"type": "object",
"properties": {
"WorkId": {
"type": "string"
},
"Owner": {
"type": [
"string",
"null"
]
},
"Note": {
"type": "string"
}
},
"required": [
"WorkId"
]
}Expected: validation succeeds; body('Parse_JSON')?['Owner'] returns null. Owner allows string or null, while only WorkId is required.
| Change to the input | Expected result | Reason |
|---|---|---|
| Omit Owner | Pass | Owner is not required. |
| Owner is null | Pass | Its allowed types include null. |
| Owner is 7 | Fail | Number is not an allowed type. |
| Note is null | Fail | Optional does not mean nullable. |
| WorkId is missing | Fail | WorkId is required. |
If Owner must always be present but may be unassigned, add Owner to required and keep its nullable type. Removing required allows the property to be missing; changing type changes which values it accepts. The type keyword can list accepted types. Do not remove every type check just to stop the error. Later steps still need data in a format they can use.
Example 4: keep IDs as text and allow decimals
An imported client ID starts with zeros. Hours can include decimals, and the task count must be a whole number. Use:
json('{"WorkId":"00042","Hours":2.5,"TaskCount":3}'){
"type": "object",
"properties": {
"WorkId": {
"type": "string"
},
"Hours": {
"type": "number"
},
"TaskCount": {
"type": "integer"
}
},
"required": [
"WorkId",
"Hours",
"TaskCount"
]
}Read body('Parse_JSON')?['WorkId']: expect the string 00042. Calculate add(body('Parse_JSON')?['Hours'], 0.5): expect numeric 3. These operations serve different purposes.
Edge tests: Hours as "2.5" fails because it is a string. Hours as numeric 2 passes a number schema. TaskCount 3.5 fails the integer check. JSON Schema distinguishes numbers, integers and numeric strings.
A schema generated from a whole-number sample may reject decimal values later. Use number if decimals are allowed. Do not cut off part of the hours just to pass an integer check. Keep IDs as strings. Once a sender changes "00042" to numeric 42, converting back to text cannot recover the original zeros unless you already know how many digits the ID should have.
Example 5: distinguish missing, null, empty and whitespace
Reuse the optional Owner schema from example 3 and test these Payload objects one at a time. Use the safe property lookup shown below to read Owner. Add a Compose named OwnerFallback:
coalesce(body('Parse_JSON')?['Owner'], 'Unassigned')| Payload Owner | OwnerFallback output |
|---|---|
| Property omitted | Unassigned |
| null | Unassigned |
| "" | Empty string |
| " " | Three spaces |
| "Mina" | Mina |
coalesce() selects a non-null value; an empty string is not null. Microsoft documents that distinction. If the business rule treats blank or whitespace-only text as unassigned, create OwnerText and then OwnerLabel:
OwnerText:
trim(coalesce(body('Parse_JSON')?['Owner'], ''))
OwnerLabel:
if(empty(outputs('OwnerText')), 'Unassigned', outputs('OwnerText'))Paste only each expression under its action label. Expected output: all four blank cases become Unassigned; Mina remains Mina. This works because the earlier schema only allows Owner to be a string or null. Do not apply the same empty-string rule to numbers or booleans; zero and false can be meaningful data.
If omitted means “leave the current owner unchanged” and null means “clear the owner,” preserve that distinction. contains(body('Parse_JSON'), 'Owner') checks whether the object has the key. It is false for an omitted property and true for an explicitly null one. Microsoft documents object-key checks with contains.
Keep a small set of sample inputs
Keep one valid input and the edge cases with the flow’s change notes. Test missing keys, nulls, empty strings, decimal numbers, IDs starting with zero and zero/one/many rows. For nested data, check the actual path. The whole response and an array inside it need different schemas.
Use json() to turn the sample text here into structured data. When a connector already returns an object or array, pass that value directly instead of double-parsing it. Microsoft’s cloud-flow cookbook explains the difference between raw JSON text and structured data. Handle malformed JSON or an empty body before this step. Allowing null in one property will not fix the whole document.
We checked these made-up examples locally for basic JSON and schema consistency. We have not run them in a Power Automate environment. Test the whole flow before changing a live schema, and handle invalid inputs before saving data or sending messages.