Skip to main content

Record ambiguous outcomes after a provider timeout

2026-11-11 16:36:27.363937+00:00
Record ambiguous outcomes after a provider timeout

A provider timeout reports that the client did not receive a timely response. It does not prove the provider rejected the action. If the remote system applied the request before the connection failed, an automatic repeat can create a duplicate.

A response can be lost after the remote action succeeds; local uncertainty needs reconciliation.

Reproduce the ambiguous outcome

This entirely local stand-in records an action and then raises a timeout:

remote_actions = {}

def provider_send(action_key, payload):
    remote_actions[action_key] = payload
    raise TimeoutError("Response lost after acceptance")

def attempt_delivery(record):
    try:
        provider_send(record["key"], record["payload"])
    except TimeoutError:
        record["state"] = "uncertain"
        return

record = {
    "key": "fixture-delivery-1",
    "payload": {"text": "Synthetic message"},
    "state": "pending",
}
attempt_delivery(record)

assert record["state"] == "uncertain"
assert record["key"] in remote_actions

The two assertions can both be true: locally uncertain and remotely applied. The fixture is useful because it forces a failure after the effect, a point that a simple "raise before sending" stub never reaches.

A real application should persist its action key and request identity before calling the provider. Store the observed timeout and attempt details without exposing sensitive payloads. The UI should show a reviewable pending or uncertain state rather than "definitely failed."

Define how uncertainty is resolved

If the provider supports looking up an action by a stable client reference, reconcile through that read operation. If it supports idempotency keys, reuse the same key according to its documented retention and payload rules. Neither feature should be assumed from the existence of an API.

When no safe lookup or duplicate-prevention contract exists, manual review may be necessary. A policy can decide how long to wait and what evidence an operator must inspect before another attempt.

Test an error before the remote action, an error after the action and a normal success. Keep those cases distinct in both application state and user messaging. Include a delayed response that arrives after the client has already entered the uncertain state.

A database transaction cannot roll back an external request. Holding the transaction open during the call only adds database locking to the unresolved network outcome.

The scheduled provider work in Postzyo offers related context. RFC 9110's HTTP semantics provide background on retrying requests and idempotent methods; actual provider guarantees still require its own contract.