redact-08-failure-recovery, after redact-07-review
What fails when AI runs locally
With a cloud API, someone else keeps the model loaded and the server up. Locally, that’s your machine’s job, and each part can be missing or slow.
The rule behind all of them is the same.
The AI is an extra, not a requirement: if it fails, you’re left with the page you prepared and the manual tools from lesson 01.
The stale-answer problem
Cancelling raises a subtle problem. You cancel, start a new analysis, and then the old one finishes. Without care, its suggestions would land on top of your new review. Redact gives each analysis a number. When an answer comes back, it’s only used if its number is still the latest; anything older is ignored. It’s a small idea that prevents a confusing class of bugs.1. Get this lesson’s code
Stop the app and the service if they’re running. From the repository root,redact-course, bring in this lesson’s commit:
2. Read the browser’s timeout
Opensrc/features/redact/analysis-client.ts.
Every request now goes through one helper:
AbortController is the browser’s way to stop a request.
This one stops for either of two reasons: the app asked it to (you cancelled), or 190 seconds passed.
The catch that follows turns every failure into a message a person can read:
TypeError.
Here that becomes “The local AI service isn’t running”.
3. Read the stale-answer guard
Opensrc/features/redact/RedactApp.tsx and find prepare.
Each run takes the next number:
await, it checks the number is still current before touching the screen:
4. Read the service’s single slot
Openqvac-service/server.ts and find createQvacService.
The service runs one analysis at a time:
withTimeout answers the request with 504 after 180 seconds.
But the model can’t be interrupted mid-page, so busy stays true until operation finishes.
Two analyses fighting over the same model would only make both slower.
5. Fail with the service stopped
Start only the app:public/test-files/redact/chat-private.png, choose Private, and select Prepare for review.
Don’t start the service.
The review screen opens anyway, with the banner The local AI service isn’t running. You can still redact by hand. and a Retry local analysis button.
There are no suggestions, but the page is there.
Drag a mark over Maya Chen, select Done, then Save copy: you get a normal chat-private.redacted.png.
6. Retry once the service is back
Select Return to review. The banner and Retry local analysis are still there: saving a copy doesn’t clear an analysis that never ran. In a second terminal, start the service:Redact QVAC service listening on http://127.0.0.1:4317, then select Retry local analysis.
After about half a minute the suggestions appear next to the mark you already drew, six of them with no direction typed, and the banner goes away.
7. Cancel a run
Select Change settings, then Prepare for review again. While the screen says Building your review copy and Reading page 1 of 1 locally…, select Continue without local analysis. The review screen opens straight away, with the banner Local analysis was cancelled. You can still redact by hand. Your page is ready for manual marks.8. See the single slot
Cancelling stopped the browser waiting, but the model may still be finishing that page. Ask the service straight away:"busy":true.
While it’s busy, any new analysis is refused:
"busy":false, and the POST now returns {"error":{"code":"invalid_request",...}}, because {} isn’t a page.
If something goes wrong
- The banner says “Local analyzer is busy”. A cancelled run is still finishing. Wait about 15 seconds, then Retry local analysis.
curlsays connection refused. The service isn’t running; start it withnpm run qvac:service.- You never see
"busy":true. The page finished before you asked. That’s fine; the second response proves the slot is free again.
9. Check your work
Stop both terminals, then:npm test reports Tests 76 passed (76).
These tests also simulate a timeout, a malformed response and a stale answer, which are hard to trigger by hand.
So what just happened?
Every way local analysis can fail now ends in the same place: your prepared page, with manual tools and a way to retry. Old answers can’t overwrite new ones, and the service never runs two analyses on top of each other.Try it yourself
Start an analysis and cancel it, then immediately select Retry local analysis. You’ll probably see “Local analyzer is busy”. Retry again after a few seconds and it works; lesson 11 makes the app wait for the slot on its own.Where to next
- Every file in this checkpoint:
redact/atredact-08-failure-recovery - About the local service and every status code in Local service API