skyl classifies every provider failure onto one of eight sentinels, retries the three that are worth retrying, and never retries the five that are not. This chapter is about branching on that classification instead of on message text.
In this chapter
- The eight sentinel errors and what each means
- How
*skyl.Errorcarries the detail, and how to reach it - How backoff works, and why jitter is not optional
- The difference between a per-attempt timeout and your own deadline
- What a rate limit means after skyl has already retried
- Why refusals and auth failures are never retried
Branch on classification#
resp, err := client.Complete(ctx, req)
switch {
case err == nil:
// ok
case errors.Is(err, skyl.ErrRateLimit):
// skyl already retried with backoff; this means it kept failing.
case errors.Is(err, skyl.ErrAuth):
log.Fatal("bad API key")
case errors.Is(err, skyl.ErrNotFound):
log.Fatal("no such model for this provider")
}resp, err := client.Complete(ctx, req)
switch {
case err == nil:
// ok
case errors.Is(err, skyl.ErrRateLimit):
// skyl already retried with backoff; this means it kept failing.
case errors.Is(err, skyl.ErrAuth):
log.Fatal("bad API key")
case errors.Is(err, skyl.ErrNotFound):
log.Fatal("no such model for this provider")
}Never match on message text. Providers reword their messages, and string matching breaks silently when they do.
| Sentinel | Message | Retried? | Meaning |
|---|---|---|---|
ErrAuth | skyl: authentication failed | Never | The credential was missing, malformed, or rejected. The same key will fail again. |
ErrRateLimit | skyl: rate limited | Yes | The provider is throttling. Retried with backoff, honouring Retry-After. |
ErrNotFound | skyl: not found | Never | The model or endpoint does not exist for this account. Because model IDs pass through unvalidated, a typo arrives here rather than failing locally. |
ErrBadRequest | skyl: invalid request | Never | The request was malformed. Often produced locally by Request.Validate. |
ErrServer | skyl: provider server error | Yes | The provider failed on its side. Retried with backoff. |
ErrUnsupported | skyl: unsupported by this provider | Never | This provider cannot express part of the request. Returned instead of silently dropping data, because a quietly discarded image looks like a model that ignored the question. |
ErrRefusal | skyl: model declined the request | Never | The model or its safety classifiers declined. The same prompt gets the same answer. |
ErrStreamClosed | skyl: stream is closed | Never | The stream was used after being closed. |
Read Error Classification for *skyl.Error, errors.As, and how HTTP statuses map onto sentinels.
Retries#
Read Retries and Backoff for the exact delay formula and why full jitter matters at fleet scale.
Timeouts#
Read Timeouts and Cancellation — WithTimeout bounds an attempt, your context bounds the call.
Rate limits#
Read Rate Limits and Retry-After for why Retry-After has its own cap, separate from the backoff ceiling.
Refusals#
Read Refusals — a refusal reaches you two different ways depending on whether it carried text.
What is never retried#
Read What Is Never Retried for the five sentinels skyl refuses to retry, and the one surprising case.
What’s next?
Start with Error Classification. If you are debugging a specific failure right now, the sentinel table above will tell you which page to jump to.