Use errors.As / errors.Is against github.com/Fabric-Labs/polyester-sdk-go/errors. Full type
list: Errors reference.
Use sdkerrors.IsRetryable(err) for backoff classification. For mutations, sdkerrors.MutationOutcomeUnknown(err) means you must reconcile state and reuse the original
request identity before retrying; it does not mean the request was rejected.
import sdkerrors "github.com/Fabric-Labs/polyester-sdk-go/errors"
_, err := client.Orders.Create(ctx, req, nil)
if err != nil {
var rl *sdkerrors.RateLimitError
var transport *sdkerrors.TransportError
var validation *sdkerrors.ValidationError
var overflow *sdkerrors.QueueOverflowError
switch {
case errors.As(err, &overflow):
// subscription only, resubscribe after catching up
case errors.As(err, &rl):
// sleep rl.RetryAfter; reconcile this create by ClientOrderID
case errors.As(err, &transport):
// outcome may be unknown; reconcile before resubmitting
case errors.As(err, &validation):
// fix input (for example PostOnly on an IOC order)
default:
if errors.Is(err, sdkerrors.ErrPolyester) {
log.Println(err)
} else {
return err
}
}
}Retry rules
- Retry transport / rate-limit failures with backoff. For a single-order create, reconcile by the
stable
ClientOrderIDbefore resubmitting; retained reuse returns a duplicate conflict, not the earlier result. - Keep replayable
requestIDandclientTriggerIDvalues stable for the same logical action. - Do not invent a new client order ID while the first create is unresolved.
- An order error code ending in
_OUTCOME_UNKNOWNmeans the change may still apply:sdkerrors.MutationOutcomeUnknownis true andsdkerrors.IsRetryableis false. Read the order or trigger back before sending it again. *TimestampSkewErrorchanged nothing and is retryable; a retry signs again.- On subscriptions,
*QueueOverflowErrormeans resubscribe after catching up, not a unary retry.
CONFLICT_DUPLICATE_CLIENT_ORDER_ID means the ID is retained. Reconcile the original create; the
conflict does not return its outcome.Cancellation and deadlines
When the call's context.Context is canceled or passes its deadline, during a read or between
retries, the error matches errors.Is(err, context.Canceled) or errors.Is(err, context.DeadlineExceeded). It is still a *sdkerrors.TransportError; its Err field holds the context error. Test for the context error before the retry logic above, and stop
rather than retry a call you canceled yourself. A mutation cut short by a deadline has an unknown
outcome: reconcile it before sending it again.
if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) {
return err // shutting down or out of time; reconcile mutations later
}Validation & precision
*ValidationError covers client-side encode failures (unknown enums, PostOnly on IOC, FOK, or
market orders, bad shapes) and order refusals that name fields; sdkerrors.Violations(err) returns
those fields. Catalog miss / excess precision usually surfaces here or as *APIError.
MFA
Step-up / elevation helpers exist for shared auth codes. API-key SDKs do not implement wallet login, MFA enrollment UX, or create-API-key session flows. sdkerrors.AuthErrorCode(err) names why
credentials were refused, on either transport. *SessionRequiredError means the method accepts only
a session token; it changed nothing and is not retryable with an API key.
Streaming errors
Unary methods return error. Subscriptions surface terminal errors via sub.Err() after the Messages() channel closes, including *QueueOverflowError. See Streaming.