Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Error Handling

All SDK operations return errors as *v1.StatusError, a typed error that carries a machine-readable Code and a human-readable Message. The SDK provides predicate functions to classify errors without importing internal packages.

StatusError

Every error returned by the SDK is a *StatusError (or wraps one). You can inspect it directly or use the convenience predicates below.

var se *v1.StatusError
if errors.As(err, &se) {
    fmt.Printf("code: %s, message: %s\n", se.Code, se.Message)
    // se.Details contains optional structured metadata
}
FieldTypeDescription
CodeErrorCodeMachine-readable error classification
MessagestringHuman-readable error description
Detailsmap[string]stringOptional structured metadata

Predicate Functions

Use these top-level functions to check error types. They work with wrapped errors via errors.As.

FunctionErrorCodeWhen it fires
v1.IsNotFoundErrorNotFoundResource does not exist (sandbox, provider, etc.)
v1.IsAlreadyExistsErrorAlreadyExistsResource with that name already exists
v1.IsConflictErrorConflictOptimistic concurrency violation or invalid state transition
v1.IsUnavailableErrorUnavailableGateway is unreachable or client is closed
v1.IsUnimplementedErrorUnimplementedOperation not supported by the gateway version
v1.IsPermissionDeniedErrorPermissionDeniedInsufficient permissions
v1.IsInvalidArgumentErrorInvalidArgumentInvalid request parameters
v1.IsDeadlineExceededErrorDeadlineExceededOperation timed out
v1.IsCancelledErrorCancelledOperation was cancelled (context cancellation)

For ErrorInternal (server-side errors), no convenience predicate exists. Match it directly via the Code field:

var se *v1.StatusError
if errors.As(err, &se) && se.Code == v1.ErrorInternal {
    fmt.Println("Internal server error:", se.Message)
}

Common Patterns

Not Found

Handle missing resources gracefully:

sb, err := client.Sandboxes().Get(ctx, "my-sandbox")
if v1.IsNotFound(err) {
    fmt.Println("Sandbox does not exist, creating...")
    sb, err = client.Sandboxes().Create(ctx, "my-sandbox", &v1.SandboxSpec{}, nil)
}
if err != nil {
    log.Fatal(err)
}

Already Exists

Guard against duplicate creation:

_, err := client.Providers().Create(ctx, &v1.Provider{
    Name: "openai",
    Type: "openai",
})
if v1.IsAlreadyExists(err) {
    fmt.Println("Provider already registered, skipping")
} else if err != nil {
    log.Fatal(err)
}

Alternatively, use Ensure for idempotent registration:

// Create-or-update in a single call
provider, err := client.Providers().Ensure(ctx, &v1.Provider{
    Name: "openai",
    Type: "openai",
})

Conflict (Optimistic Concurrency)

When two clients modify the same resource concurrently, the second write receives a conflict error. Retry by re-reading the resource:

sb, _ := client.Sandboxes().Get(ctx, "my-sandbox")

result, err := client.Sandboxes().AttachProvider(ctx,
    "my-sandbox", "openai", sb.ResourceVersion,
)
if v1.IsConflict(err) {
    // Another client modified the sandbox — re-read and retry
    sb, _ = client.Sandboxes().Get(ctx, "my-sandbox")
    result, err = client.Sandboxes().AttachProvider(ctx,
        "my-sandbox", "openai", sb.ResourceVersion,
    )
}
if err != nil {
    log.Fatal(err)
}

Unavailable

Handle gateway connectivity issues:

sb, err := client.Sandboxes().Get(ctx, "my-sandbox")
if v1.IsUnavailable(err) {
    fmt.Println("Gateway is not reachable, check connection")
    // Implement retry with backoff
}

Unimplemented

Detect unsupported operations gracefully:

_, err := client.Sandboxes().GetLogs(ctx, "my-sandbox")
if v1.IsUnimplemented(err) {
    fmt.Println("Log retrieval not supported by this gateway version")
}

Retry with Backoff

For transient errors like Unavailable or DeadlineExceeded, use exponential backoff:

func withRetry(ctx context.Context, maxAttempts int, fn func() error) error {
    backoff := 100 * time.Millisecond

    for attempt := 0; attempt < maxAttempts; attempt++ {
        err := fn()
        if err == nil {
            return nil
        }

        // Only retry transient errors
        if !v1.IsUnavailable(err) && !v1.IsDeadlineExceeded(err) {
            return err
        }

        // Add jitter to prevent thundering herd after outages
        jitter := time.Duration(rand.Int63n(int64(backoff) / 2))

        select {
        case <-ctx.Done():
            return ctx.Err()
        case <-time.After(backoff + jitter):
            backoff *= 2
        }
    }
    return fmt.Errorf("exhausted %d retry attempts", maxAttempts)
}

Usage:

var sb *v1.Sandbox
err := withRetry(ctx, 3, func() error {
    var e error
    sb, e = client.Sandboxes().Get(ctx, "my-sandbox")
    return e
})

Context Cancellation

All SDK methods accept a context.Context. Use context deadlines and cancellation to control timeouts:

// 5-second timeout for a single call
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel()

sb, err := client.Sandboxes().Get(ctx, "my-sandbox")
if v1.IsDeadlineExceeded(err) {
    fmt.Println("Request timed out")
}

See also: Testing for how the fake client returns the same error codes.