The request timed out. Did the server save the change?
The user clicks Save. A spinner runs for a while, then the request times out. It is tempting to show “Save failed” and a Retry button. For a write, that may leave out the hardest part: the browser does not necessarily k
The user clicks Save. A spinner runs for a while, then the request times out.
It is tempting to show “Save failed” and a Retry button. For a write, that may leave out the hardest part: the browser does not necessarily know whether the operation was applied.
This is a design walkthrough for a hypothetical admin action. The same question appears in imports, saved items, and other operations that change server state.
The response is evidence about an operation
Consider three possible sequences:
| Server outcome | What the browser receives |
|---|---|
| The request never reaches the handler | No successful response |
| The handler rejects the operation | A rejection, if the response arrives |
| The server commits, then the response is lost | No successful response |
The first and third can look the same to the client. A transport failure is therefore not enough evidence to claim that nothing changed.
I would represent at least these outcomes in the UI:
- Confirmed success: the operation was applied.
- Confirmed rejection: the operation was not accepted under the server's contract.
- Unknown outcome: the client cannot yet establish what happened.
The third state needs an explanation and a recovery path. It should not pretend to be either of the first two.
Make the attempted operation identifiable
For an operation that must avoid duplicate application, one possible contract uses a client-generated operation identifier.
operation: op-123
action: import the reviewed file
payload: the approved plan
The server associates the identifier with the intended payload and result. A retry using the same identifier can refer to the same logical operation rather than create a new one.
That is a proposed application contract, not behavior supplied automatically by a custom request header. It needs implementation: concurrent retries, payload mismatches, authorization, retention, and failure recovery all matter.
For example, accepting the same identifier with different import contents would make it difficult to know what was retried. The server should define what it accepts and how it reports a mismatch.
Recover the outcome
A status endpoint can let the browser ask about op-123. A fresh read can also work when the application can reliably identify the attempted change. Merely seeing a similar record may be insufficient when concurrent users can create similar data.
During reconciliation, a message such as “We couldn't confirm the import. Checking its status…” describes the available evidence.
If the server is still working, show that state. If it committed, show the result. If the outcome remains unknown, explain which action is safe instead of inventing a result.
Retries need a defined effect
HTTP semantics defines idempotent methods in terms of their intended server effect when identical requests are repeated. That helps when deciding what a client can safely retry, but an operation's application-level behavior still needs attention.
A retried “create another import” command and a retry of one identified import do not express the same intent.
I would choose the retry policy at the same time as the endpoint contract, rather than bolt it onto a generic error handler.
Cancel has its own meaning
Closing a dialog or aborting a fetch stops part of the client interaction. It does not establish that a server-side write was reversed.
A long-running operation may support a cancellation request. That request also has an outcome: the job might stop before applying anything, stop after partial work, or finish before cancellation is accepted.
The UI needs to describe the actual result. “Cancellation requested” is a more limited statement than “Nothing was imported.”
The failure test worth running
Let the server apply the operation, then drop the response. Reload the page and retry the same operation from another tab.
Check whether the application can recover one authoritative result, whether the user understands the state, and whether the retry creates duplicate work.
This is where a reliable workflow separates itself from a demo that only handles a successful response. What does your application show when the write may have succeeded but the browser cannot prove it yet?
Originally published by Dev.to WebDev. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.