Empty JSON field names from /api/v1/workflows - Powershell: "The provided JSON includes a property whose name is an empty string"

When exporting workflows via the API, Powershell ( 7.5.0 Core) is unable to parse the returned JSON. There are some "" fields in the output.

Does anybody have a workaround for this?

Demo script:

param (
      [Parameter(Mandatory = $true)] [string] $n8nApiKey
    , [Parameter(Mandatory = $true)] [System.Uri] $n8nUrl
    , [Parameter(Mandatory = $true)] [System.IO.FileInfo] $OutputFolder
)


#foreach

$headers  = @{
    'X-N8N-API-KEY' = $n8nApiKey
    accept = 'application/json'
}

# This should automatically be converted to an object, instead, I get a string
$ret = Invoke-RestMethod -Method Get -Uri ("{0}api/v1/workflows?&excludePinnedData=true&limit=250" -f $n8nUrl.AbsoluteUri) -Headers $headers -ContentType 'application/json' 

Write-Host $ret.GetType().Name
$workflows = ConvertFrom-Json -InputObject $ret
foreach($workflow in $workflows.data) {

    $filePath = Join-Path -Path $OutputFolder -ChildPath ("{0}.json" -f ($workflow.name -replace ':',"_"))
    $workflow | ConvertTo-Json -Depth 100 | Out-File $filePath
}

Normally, Invoke-RestMethod would automatically convert the result to an object. Instead, I get a string. When I try to convert that string, I get this exception from ConvertTo-Json.

$workflows = ConvertFrom-Json -InputObject $ret
     |               ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     | The provided JSON includes a property whose name is an empty string, this is only supported using the -AsHashTable switch.

This "" seems to be the offending section:

				{
					"parameters": {
						"curlImport": "",
						"httpVariantWarning": "",
						"method": "POST",
						"url": "https://api.crowdstrike.com/identity-protection/combined/graphql/v1",
						"": "",
						"authentication": "predefinedCredentialType",
						"nodeCredentialType": "crowdStrikeOAuth2Api",
						"provideSslCertificates": false,
						"sendQuery": false,
						"sendHeaders": false,
						"sendBody": true,
						"contentType": "json",
						"specifyBody": "json",
						"jsonBody": "={{ $json.toJsonString() }}",
						"options": {},
						"infoMessage": ""
					},

Information on your n8n setup

Debug info

core

  • n8nVersion: 1.86.1
  • platform: docker (self-hosted)
  • nodeJsVersion: 20.19.0
  • database: sqlite
  • executionMode: regular
  • concurrency: -1
  • license: enterprise (production)

Hi @hukel,

Did you try to use Invoke-WebRequest to get the raw response content first? then continue the other process…

Invoke-WebRequest would give me the raw string (same string I already have). . The problem isn’t in the HTTP transfer of the string. The problem that the string is not (Powershell) valid JSON. I get the same exception, even if I save the JSON to a text file and try parsing it.

I just did a UI-based export of the same workflow, these empty field names are there, too. This seems to be a peculiarity of the Crowdstrike HTTP node type’s parameters field.

      "parameters": {
        "curlImport": "",
        "httpVariantWarning": "",
        "method": "POST",
        "url": "https://api.crowdstrike.com/alerts/entities/alerts/v2?include_hidden=true",
        "": "",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "crowdStrikeOAuth2Api",
        "provideSslCertificates": false,
        "sendQuery": true,
        "specifyQuery": "keypair",
        "queryParameters": {
          "parameters": [
            {
              "name": "include_hidden",
              "value": "true"
            }
          ]
        },
        "sendHeaders": false,
        "sendBody": true,
        "contentType": "json",
        "specifyBody": "json",
        "jsonBody": "={\n  \"composite_ids\": {{JSON.stringify($json.resources)}}\n}",
        "options": {},
        "infoMessage": ""
      },

Workaround:

param (
      [Parameter(Mandatory = $true)] [string] $n8nApiKey
    , [Parameter(Mandatory = $true)] [System.Uri] $n8nUrl
    , [Parameter(Mandatory = $true)] [System.IO.FileInfo] $OutputFolder
)


$existingFiles = @{}
foreach($file in Get-ChildItem -Path "$OutputFolder\*.json") {
    $workflow = Get-Content $file | ConvertFrom-Json -AsHashtable
    $existingFiles[$workflow.id] = $file
}

$headers  = @{
    'X-N8N-API-KEY' = $n8nApiKey
    accept = 'application/json'
}

# This should automatically be converted to an object, instead, I get a string
$ret = Invoke-RestMethod -Method Get -Uri ("{0}api/v1/workflows?&excludePinnedData=true&limit=250" -f $n8nUrl.AbsoluteUri) -Headers $headers 

# n8n sometimes returns "objectionable" (to Powershell) JSON
if($ret -is [string]){
    $workflows = (ConvertFrom-Json -InputObject $ret -AsHashtable).data
} else {
    $workflows = (ConvertFrom-Json -InputObject $ret).data
}

foreach($workflow in $workflows) {

    $fileName = ("{0}.json" -f ($workflow.name -replace ':',"_"))
    $filePath = Join-Path -Path $OutputFolder -ChildPath $fileName
    $existingFile = $existingFiles[$workflow.id]
    if(     $null -ne $existingFile `
        -and $fileName -ne $existingFile.Name) {
            Write-Host "Renaming $existingFile to $fileName"
            Move-Item $existingFile -Destination $filePath

            #TODO git rename
    }

    # Get the raw string here,  don't want any JSON distortion caused by Powershell/.NET
    $ret = Invoke-WebRequest -Method Get -Uri ("{0}api/v1/workflows/{1}" -f $n8nUrl.AbsoluteUri,$workflow.id) -Headers $headers 

    # Overwrite the content
    $ret.Content | Out-File $filePath
}

#TODO git status check