Skip to content

Cancel messages can be published with empty <references> #106

Description

@johnswarbrick

Some Cancel/Remove messages go out with an empty <references/>, so consumers cannot tell which alert is being cancelled.

Example: message 125772 (Remove, TargetAreaCode 034FWFMAEDWINOLL) has empty references:

<alert xmlns="urn:oasis:names:tc:emergency:cap:1.2">
<script/>
<script/>
<identifier>125772</identifier>
<sender>www.gov.uk/environment-agency</sender>
<sent>2026-08-31T07:20:24+00:00</sent>
<status>Actual</status>
<msgType>Cancel</msgType>
<source>Flood warning service</source>
<scope>Public</scope>
<references/>                                <---------------- HERE
<info>
<language>en-GB</language>
<category>Met</category>
<event>
<![CDATA[ Remove ]]>
</event>
<responseType>AllClear</responseType>
<urgency>Immediate</urgency>
<severity>Minor</severity>
<certainty>Likely</certainty>
<expires>2026-09-01T07:20:24+00:00</expires>
<senderName>Environment Agency</senderName>

Other remove messages such as 125788, sent three minutes later, correctly references its original alert:

<alert xmlns="urn:oasis:names:tc:emergency:cap:1.2">
<script/>
<script/>
<identifier>125788</identifier>
<sender>www.gov.uk/environment-agency</sender>
<sent>2026-08-31T07:23:13+00:00</sent>
<status>Actual</status>
<msgType>Cancel</msgType>
<source>Flood warning service</source>
<scope>Public</scope>
<references>www.gov.uk/environment-agency,125764,2026-08-30T23:25:16+00:00</references>
<info>
<language>en-GB</language>
<category>Met</category>
<event>
<![CDATA[ Remove ]]>
</event>
<responseType>AllClear</responseType>
<urgency>Immediate</urgency>
<severity>Minor</severity>
<certainty>Likely</certainty>
<expires>2026-09-01T07:23:13+00:00</expires>
<senderName>Environment Agency</senderName>

Root cause: in lib/functions/processMessage.js, buildReference only writes the reference when the prior message is still live (i.e. has not expired):

if (lastMessage && lastMessage.expires > new Date()) { ... }
return ''

That guard is sensible for chaining Alert to Update, but it is also applied to Cancels. When the warning being withdrawn has already expired, the reference is dropped and the Cancel goes out with nothing identifying what it cancels.

Impact: an empty-references Cancel passes the base CAP v1.2 schema but is semantically incomplete, and it fails the common CAP profiles that require references on Update and Cancel. Consumers that key on references cannot action the withdrawal, so a cancelled warning can remain active downstream.

Proposed fix: for msgType === 'Cancel', always write the reference to the prior message (drop the expires > new Date() check on the cancel path), so a Remove always identifies the alert it withdraws.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions