When an agent cannot do what was asked, it has three options. It can keep spinning and hope the problem goes away. It can produce something that looks like the answer. Or it can stop and say exactly what happened. The first wastes the user's time, the second wastes their trust, and only the third gives them something to act on. Treating a refusal as a legitimate outcome, with its own design, is one of the cheapest reliability improvements available.
What fabricated progress looks like
It rarely looks like lying. It looks like helpfulness under pressure:
- A progress bar that keeps moving after the background job has already died.
- A task marked done with no file, no diff and no sources attached.
- A summary of a document the agent never managed to open, written from the title and the question.
- A citation with a plausible URL that nobody retrieved.
- "No issues found" when the search tool timed out and returned nothing.
- A retry loop that swallows the same error five times and reports the partial output of the sixth attempt as complete.
Each of these turns a recoverable situation into a hidden one. The user moves on, builds on the output and discovers the gap later, when it costs more.
Nothing found, unavailable and not allowed are different answers
The most useful distinction is between outcomes that sound alike but mean different things:
| Outcome | What it means | What the user should do |
|---|---|---|
| Nothing found | The search ran over the intended sources and nothing matched. | Accept it, or widen the question deliberately. |
| Unavailable | The search, tool or service could not run. | Retry later or route around it; do not read it as "none". |
| Not allowed | The action needs authority this run does not have. | Ask the right person, or narrow the scope. |
| Rejected | A person looked at the request and said no. | Treat it as a decision; revise the plan instead of looking for another path. |
An empty result is a valid answer. "Unavailable" is not an empty result. Collapsing the two is how a team ends up confidently reporting that a contract has no termination clause because the document index was down that afternoon. Missing evidence is an open question goes further into keeping those gaps visible.
Different failures need different remedies
A generic "something went wrong" forces the user to guess. Most failures fall into a handful of categories, and each one suggests its own next step:
- Malformed input. The request itself is invalid: a missing field, an unreadable file. Fix the input; retrying unchanged will fail again.
- Missing authority. The run lacks the permission or approval it needs. Get the approval or change the scope; retrying will not help.
- Payment or credit state. The budget is used up or the spending ceiling was reached. Raise the limit deliberately or wait for the next period. Put a ceiling on agent spend explains why stopping is the right behavior here.
- Conflict. Someone else changed the thing, or the same work is already running. Re-read the current state before trying again.
- Unavailable dependency. A tool or service is down. Retry later, with backoff, and say so in the meantime.
Only the last category is usually safe to retry automatically. Retrying the others either repeats the failure or, worse, finds a way around a check that was there on purpose.
The anatomy of a refusal people can use
A good refusal answers five questions: what was attempted, why it stopped, what was not done, what the person can do next and whether it is safe to try again. Something like this, in whatever format your interface uses:
status: refused
reason: missing_authority
attempted: export 3 files from project "Q3 review" to external storage
not done: no files left the workspace
next: ask a project owner to approve the export, or narrow the file list
retry: after approval; this request will not run again on its own
The "not done" line is the one most designs forget, and it is the one that lets the user stop worrying. The "retry" line prevents the opposite mistake: a user who clicks again and again, hoping persistence will substitute for permission.
How GoCux treats a refusal
GoCux treats an honest refusal as useful product behavior rather than an embarrassment to hide. Its error responses keep missing authority, malformed input, conflicts, payment state and unavailable dependencies apart, because each has a different fix; the warnings and errors reference covers them. If durable execution cannot be provided, the request is refused where you can see it instead of showing progress that is not happening. An accepted work order is not presented as proof that the work ran either: you read its state, progress and attachments on the task record.
The same rule runs through the evidence surfaces. In knowledge search, an empty result is a real answer, and "unavailable" is reported as something different from "nothing found". When the daily credit cap is reached, work stops and says why. When a person rejects a gated action, the rejection is recorded and the work does not slip through by another route; why an agent should ask before it acts describes that gate.
A checklist for refusal states
- Every long-running operation has a terminal state other than "done": refused, unavailable, rejected, stopped.
- "Nothing found" and "could not search" are different messages with different wording.
- Errors name their category, and each category has a stated next step.
- Every refusal says what was not done, so the user knows what did not happen.
- Automatic retries are limited to unavailable dependencies; authority and input errors never retry silently.
- A "done" state requires an attached result: no artifact, no done.
- Refusals and rejections are recorded with the work, so the trail shows what stopped and why.
Bring this workflow into your workspace.
Set the scope. Review the evidence. Approve the steps that need it.