is_usable
EveryFrameProcessor reports whether it can still work:
- STT and TTS services stop accepting work, instead of failing once per chunk of audio or piece of text
- WebSocket services stop reconnecting, instead of retrying credentials the provider has already rejected
- A
ServiceSwitcherfails over to another provider
frame.processor.is_usable sees the verdict that came with the error it’s handling.
ErrorCategory
Errors carry a category saying what kind of failure it was, independent of which provider produced it:AUTHENTICATION, AUTHORIZATION, and INVALID_REQUEST are permanent: retrying gives the same result until credentials or settings change. Reporting one is what makes a processor unusable. Read category.is_permanent rather than listing the three yourself.
APPLICATION is how a service reports a failure in code it invoked — a tool
handler, say. Those failures say nothing about the service’s own health, so
they never make it unusable.Deciding what the pipeline does
PipelineWorker takes a policy for what to do when a processor becomes unusable:
The policy is applied once per processor, not once per failed request — an unusable processor keeps failing for as long as the pipeline uses it, and the decision only needs making once.
CONTINUE leaves the decision to your application, which is the right default when a ServiceSwitcher will fail over, or when the bot can carry on degraded. Choose END when a bot with a dead service has nothing useful left to do; the Pipecat examples use it, so an example stops rather than running on with a service that will never answer.
Reacting in your application
Handleon_error to decide for yourself:
on_usable_changed fires whenever a processor’s verdict changes, in either direction:
Recovering a service
An unusable processor stays that way until you say otherwise. Once whatever broke has been fixed — new credentials, a corrected voice ID — bring it back:ServiceSwitcher will consider it a candidate. Switching to a service that is still marked unusable is refused, so recovery has to come first.
Reporting errors from your own processor
category marks the processor unusable. When you know the processor is finished but the category doesn’t say so on its own — a retry budget exhausted, for instance — pass force_treat_as_permanent=True instead of inventing a category.
Implement _classify_error() to map exceptions your processor understands onto a category. Errors carrying an HTTP status are classified from it automatically: 401 to AUTHENTICATION, 403 to AUTHORIZATION, 429 to RATE_LIMIT, 5xx to SERVER.
Migrating from fatal errors
ErrorFrame.fatal, the fatal argument of push_error(), and FatalErrorFrame are deprecated and will be removed in 2.0.0. A fatal error cancelled the pipeline outright, which conflated “this service is broken” with “this run is over” and left applications no say in the matter.
Which replacement you want depends on what the error meant:
The error leaves its processor unable to work. Report that, and let the policy decide:
ProcessorUnusablePolicy.CANCEL reproduces what fatal=True did. END and CONTINUE are usually better.
The error isn’t about a processor’s state, but the pipeline should stop. Push the error, then end the pipeline explicitly:
EndWorkerFrame to drain queued frames, or CancelWorkerFrame to abandon them.
To read errors, replace if frame.fatal: with a check on the processor:
Related
Service Switching
Fail over to another provider automatically
PipelineWorker
Pipeline events, including on_pipeline_error
Frame Processor Events
on_error and on_usable_changed
System Frames
ErrorFrame and its fields