Errors, Refusals and Empty States
By the Syncfusion DevTutorials team · Last updated
Timeouts, tool failures, refusals, rate limits and "I don't know" are screens you design. Give each one plain words and a next step, then export your v1 AI UX kit.
When the AI Fails
Designed state = a screen you planned for a failure: what happened, what is safe, and what to do next.
In this lesson, you will design a screen for each of the five ways an AI feature fails. Then you will export your v1 AI UX kit.
AI features fail more often than normal buttons. Tools time out. Services break. The model refuses. The provider says "too many requests". Sometimes there is simply no answer.
Most apps show the raw error, or nothing at all.
Press Play, or use Next to go one step at a time.
- Frames 1 to 5: one failure each. The left side shows the raw error. The right side shows a designed state.
- The "What this shows" panel: what happened, and the next step.
- Frame 6: five failures, five different screens.
Simple lesson: a failure is a screen you design, not an accident you show.
A Real-World Example
Think about a cash machine.
- When it runs out of notes, it does not show "ERR_CASSETTE_EMPTY". It says "Unable to dispense cash. Your account has not been charged."
- When the network is down, it says "Service unavailable. Please use another machine."
- When you enter the wrong PIN, it says how many tries are left.
Each message says what happened, what is safe, and what to do.
AI failures need the same care:
- "Your account has not been charged" = what is safe.
- "Please use another machine" = the next step.
- A different message for each problem = a designed state per failure.
Click the button. The Build tab plays a tool failure through the naive interface.
- The screen: "TypeError: Cannot read properties of undefined".
- The Scenario list: five failures to try, one by one.
- The checks: each raw error fails.
Simple lesson: a support agent cannot act on a stack trace.
What You Will Learn
In this lesson, you will learn:
- The five ways an AI feature fails.
- The three parts of a designed state.
- Why one generic message is not enough.
- Why a blank screen is the worst answer.
- How to export your v1 AI UX kit.
About failures in this lab
- Each failure scenario in the Build tab sends one
errorevent with a raw message, like a real service would. - Your interface decides what the user sees: the raw message, nothing, or a designed state.
- The raw messages are real-looking on purpose. Real services send text like this every day.
Five Ways AI Fails
| Failure | What happened | A good next step |
|---|---|---|
| Timeout | A tool or the model took too long | Try again; the question is saved |
| Tool failure | A service the agent uses broke | Try again; nothing was changed |
| Refusal | The model said no on purpose | Ask about something it can help with |
| Rate limit | Too many requests right now | Retry after the wait, and say how long |
| "I don't know" | There is no answer in the data | Check the input, or search another way |
Refusal = the model declines a request on purpose, for example to protect private data.
Rate limit = a provider's cap on how many requests you can send in a time window.
Simple lesson: five failures, five different causes, five different next steps.
The Three Parts of a Designed State
Every designed state has three parts:
- What happened, in plain words. "We could not open the order."
- What is safe. "Nothing was changed." "Your question is saved."
- What to do next, as one button that starts with a verb. "Try again." "Search orders."
const AI_STATES = {
tool: { title: "We could not open the order", body: "The order system had a problem. Nothing was changed.", action: "Try again" },
rate: { title: "The assistant is busy", body: "Your question is saved and will be ready to send in 20 seconds.", action: "Retry in 20 s" },
};
function AiState({ code, onAction }: { code: keyof typeof AI_STATES; onAction: () => void }) {
const s = AI_STATES[code];
return (
<div role="status">
<strong>{s.title}</strong>
<p>{s.body}</p>
<button onClick={onAction}>{s.action}</button>
</div>
);
}
Log the raw error for your team. Show the designed state to the user.
Easy way to remember: What happened? What is safe? What now? Every failure screen answers all three.
Simple lesson: plain words, what is safe, one helpful button.
One Message Is Not Enough
Many apps use one message for everything: "Something went wrong. Please try again."
But "try again" does not fix every failure.
Step through both frames.
- Frame 1: a rate limit. Trying again at once fails again, because the limit has not reset.
- Frame 2: a refusal. Trying again is refused again, because the request itself is the problem.
Simple lesson: each failure needs its own next step.
The Blank Screen
The worst failure screen is no screen at all. The spinner disappears and nothing replaces it. The user does not know if it worked, failed or is still running.
Step through all three frames.
- Frames 1 and 2: on the left, a spinner. On the right, the slow step is named.
- Frame 3: the request times out. The left side goes blank. The right side says the question is saved, with Try again.
"I don't know" is a good answer, if you design it.
- An agent that makes something up is worse than one that says it found nothing.
- Give "I don't know" its own screen with a next step, so it does not feel like a failure.
- Lesson 3's "not sure" state is the gentle version of this.
Simple lesson: never leave the user with nothing.
Lab Exercise: Design Every Failure
Your task: change the Lesson 6 choices in the Build tab so all five failure scenarios pass. Then press Check all scenarios.
Click the button and open the Build tab.
- Each failure: choose "Designed state with a next step".
- Play each scenario: compare the designed state with the raw one.
- A failing check: quotes what the user would see instead.
Simple lesson: five designed states, one per failure.
Export Your v1 AI UX Kit
You have now built the whole first half of the course:
| Lesson | What you built |
|---|---|
| 2 | Streaming, tool steps, a skeleton and a real Stop |
| 3 | Citation chips, a "not sure" banner and a scope note |
| 4 | Stop, Edit, Retry with versions, and snapshot Undo |
| 5 | A preview card, confirm by risk and an undo window |
| 6 | A designed state for every failure |
v1 kit = a zip with React components for each pattern, a config file with your choices, and the lab's checks as Playwright tests.
Click the button and open the Export tab.
- The table: which lessons pass every check with your interface.
- The file list: read each file before you download.
- Download: saves
ai-ux-kit-v1.zip. The components need no library.
The Playwright tests start as "fixme". Point each one at your own app, then remove "fixme". Test them against your stack: the lab's rules are the same, but your app's details are not.
Simple lesson: your kit is the five lessons as code you can use.
What Changes per Framework
| Option | Error and empty states |
|---|---|
| Hand-built React | A state per error code, as above |
| CopilotKit | Catch errors from the agent run and render your own state |
| Vercel AI SDK UI | useChat exposes an error; map it to your states |
| Syncfusion AI components | Templates for the reply area (optional, not graded) |
Build It in React
In the Code tab, failures arrive as an error event with a code and a raw message. The starter prints raw on the screen. Your version logs raw for the team and shows a designed state for the user.
if (e.type === "error") { console.error(e.raw); setCode(e.code); }
Write one state per code: a title, what is safe, and a button for the next step.
TL;DR
- Designed state = a planned screen for a failure.
- Five failures = timeout, tool failure, refusal, rate limit, "I don't know".
- Three parts = what happened, what is safe, what to do next.
- One message is not enough = each failure needs its own next step.
- Never blank = the user must always see something.
- v1 kit = your Lesson 2 to 6 interface as React components and tests.
Common mistakes
| Mistake | Wrong idea | Correct idea | What goes wrong |
|---|---|---|---|
| Showing the raw error | "It's honest." | Log it; show plain words. | Users see "TypeError" and lose trust. |
| One message for everything | "Something went wrong" covers it. | One state per failure. | "Try again" fails again on a rate limit. |
| A blank panel | "The spinner stopped, so it's done." | Always show a state. | Users wait, then give up. |
| Not saying what is safe | "They know nothing happened." | Say "Nothing was charged" or "Your question is saved". | Users worry, and retry a payment twice. |
| Hiding "I don't know" | "Always give an answer." | Design the no-answer state. | The agent makes something up. |
Check Your Understanding
Exercises
What to Remember
- AI fails more often than normal buttons. Plan for it.
- Five failures: timeout, tool failure, refusal, rate limit, "I don't know".
- Each designed state says what happened, what is safe and what to do next.
- One generic message sends people in circles.
- Never leave the screen blank.
- Export your v1 kit and wire its tests to your own app.
Further Reading
- Error-message guidelines, by Nielsen Norman Group: visible, plain and helpful error messages.
- People + AI Guidebook: Errors and Graceful Failure, by Google PAIR: how AI products should fail.
Next: Controlled Generative UI, where the agent stops writing walls of text and picks from your own components: an order card, a refund form and a tracking timeline.