Ignition Message Handlers Have No HTTP POST Endpoint

Daniel Price6 min read
HMI / SCADAOther ManufacturerTroubleshooting
Licensed PE Working through this on a live machine? A Maine-licensed engineer can take it from here — included with IMD hardware, by the hour for everything else. Book an engineer

The Azure pipeline POST to http://<My-IP>:8088/system/gateway/message/global-project/RunCSVImport never reached the RunCSVImport gateway message handler because Ignition has no HTTP route that dispatches message handlers. Handlers are invoked only from an already-connected context: another gateway over the persistent gateway network websocket, or a client or Designer with an established session using RPC. An external HTTP caller needs an endpoint you build with the WebDev module. That endpoint then does the work or relays the call to the handler.

Does the pipeline agent reach the gateway on TCP 8088?

Check the lowest layer first. The CSV/JSON file transfer already succeeds, but it may use a different port or protocol than the HTTP call. From the same agent that runs the pipeline step, run:

Test-NetConnection -ComputerName <My-IP> -Port 8088
Result Meaning Next check
TcpTestSucceeded : True The VM network security group, the OS firewall and the gateway web server all accept the connection Inspect the HTTP response
TcpTestSucceeded : False A hop is dropping traffic. Suspects are the Azure NSG, the Windows or Linux firewall on the VM, a Microsoft-hosted agent with no route to a private IP, or a gateway bound to another port Open the path or move to a self-hosted agent inside the VNet, then retest

In this installation the step completed without errors, so the TCP path is almost certainly open. A clean pipeline run only proves that some HTTP exchange finished. It does not prove that any Ignition script ran.

What status and body does the gateway actually return?

Invoke-RestMethod throws only on transport failures and 4xx/5xx status codes. It returns quietly on any 2xx or followed redirect. If the gateway's web server answers an unknown /system/... path with a web page or a redirect, the step goes green and nothing executes. Swap in Invoke-WebRequest once to see what the gateway really sends back:

$h = @{ Authorization = ("Basic {0}" -f $base64AuthInfo) }
try {
  $r = Invoke-WebRequest -Uri $url -Method POST -Headers $h `
       -ContentType "application/json" -Body "{}" -UseBasicParsing -MaximumRedirection 0
  Write-Host "Status: $($r.StatusCode)  Type: $($r.Headers['Content-Type'])"
  Write-Host $r.Content.Substring(0, [Math]::Min(500, $r.Content.Length))
} catch {
  Write-Host "HTTP error: $($_.Exception.Response.StatusCode)"
  throw
}
Observed response Interpretation
404 / error status No servlet owns the path. Build the endpoint.
2xx or 3xx with HTML content The gateway web UI answered. No script ran. Build the endpoint.
2xx with the JSON your own endpoint returns The endpoint exists. Move on to handler and logging checks.

Either of the first two results points to the same cause: the /system/gateway/message/... path is not an Ignition API.

Why can't a raw HTTP POST reach a gateway message handler?

Message handlers sit behind Ignition's internal messaging layer, not the gateway's public web server. system.util.sendMessage and system.util.sendRequest travel in one of two ways:

  • over the persistent gateway network websocket, from one gateway to another, or
  • over the RPC channel of a Vision client, Perspective session or Designer that has already authenticated to the gateway.

An Azure agent holds neither kind of connection. Basic Auth on an HTTP request does not create a session that the messaging layer recognizes. The handler is correct, but nothing ever delivers a message to it.

Caller Transport Can invoke a gateway message handler?
Another Ignition gateway Gateway network websocket Yes, with sendMessage/sendRequest to the remote server
Client / Designer / Perspective session Established session RPC Yes
Azure pipeline, curl, any external tool Plain HTTP(S) Only through a WebDev endpoint you create

Is the WebDev module installed and the target project runnable?

In the gateway web interface, open the modules page and confirm WebDev is installed and licensed. If it is missing, install it before continuing. Then check the project:

  • Put the WebDev resource in a runnable, enabled project. A project marked inheritable-only does not execute its own gateway-scoped resources.
  • Put the RunCSVImport message handler in the same runnable project, or in a parent that this project inherits from. sendMessage addresses a project by name, and the handler only fires in a project that actually runs.
  • Every POST from outside passes through WebDev resource security. Set the authentication user source and required roles there. The IGN_USER/IGN_PASS pipeline variables must match a user in that source.

How do you build the WebDev endpoint that fires RunCSVImport?

  1. In the Designer, open the project and create a Python resource under WebDev named RunCSVImport.
  2. Implement doPost. The simplest approach keeps the existing handler and relays to it:
    def doPost(request, session):
        logger = system.util.getLogger("RunCSVImport")
        body = request['data'] or {}   # parsed JSON when Content-Type is application/json
        logger.info("WebDev RunCSVImport called, body: %s" % str(body))
        system.util.sendMessage("global-project", "RunCSVImport", payload=body, scope="G")
        return {'json': {'status': 'dispatched'}}
    sendMessage is fire-and-forget, so the handler runs on its own thread. If the pipeline must know whether the import succeeded, either call system.util.sendRequest and return the handler's result, or run the import code directly inside doPost and return a status.
  3. Under the resource's security settings, require authentication against the user source that holds the pipeline account. Enable the require-HTTPS option once the gateway has a certificate, so the Basic Auth header is not sent in clear text.
  4. Save and publish the project.
  5. Point the pipeline at the WebDev path, which follows /system/webdev/<project>/<resource>:
    $url = "http://<My-IP>:8088/system/webdev/global-project/RunCSVImport"
    $r = Invoke-RestMethod -Uri $url -Method POST -Headers $h `
         -ContentType "application/json" -Body '{"file":"import.csv"}'
    if ($r.status -ne 'dispatched') { throw "Unexpected response: $($r | ConvertTo-Json)" }
    The explicit check makes the pipeline fail when an HTML page or other unexpected body comes back. That closes the silent-success gap that hid the original problem.

If the file content is small, you can also POST the CSV/JSON itself in the body and have doPost process it. That removes the separate file-copy step and the timing risk between the copy and the trigger.

How do you confirm each hop fired?

  1. Run the diagnostic Invoke-WebRequest against the new URL. Expect status 200, content type application/json and the body {"status":"dispatched"}. A 401 or 403 means the WebDev security settings reject the pipeline account.
  2. In the gateway web interface, open the log viewer under status/diagnostics and filter on the logger name RunCSVImport. Two entries should appear per call: WebDev RunCSVImport called from doPost, then Message Handler Triggered! and Payload received: from the handler.
  3. If the WebDev line appears but the handler lines do not, recheck three things. The project name passed to sendMessage must be spelled exactly. The handler name must match RunCSVImport case for case. The handler's project must be runnable, not inheritable-only.
  4. Replace the logging stub with the real tag-creation logic. Rerun the pipeline and confirm the tags defined in the imported file appear in the tag browser.

FAQ

Why does my Azure pipeline succeed but the Ignition message handler never runs?

Invoke-RestMethod fails only on transport errors or 4xx/5xx codes. A 2xx or redirect from the gateway web server counts as success even when no script executed. Log the status code and content type, and validate the response body so the step fails when it gets HTML instead of your endpoint's JSON.

Why does Ignition not expose gateway message handlers over HTTP?

Message handlers are dispatched through the gateway network websocket or an authenticated client/Designer RPC session. Plain HTTP requests do not enter that messaging layer. External systems need a WebDev resource, whose doPost can call system.util.sendMessage or sendRequest.

What URL does an Ignition WebDev POST endpoint use?

WebDev resources are served under /system/webdev/<project>/<resource> on the gateway's web port, for example http://<My-IP>:8088/system/webdev/global-project/RunCSVImport. The project must be runnable and the resource must define doPost.

Why does the WebDev endpoint log a call but the message handler stays silent?

The project name or handler name passed to system.util.sendMessage does not match exactly, or the handler lives in an inheritable-only project that never runs its own gateway scripts. Correct the names, move the handler into a runnable project, and look for Message Handler Triggered! under the RunCSVImport logger.

Back to blog