clawdforge/clients/go/errors.go
Kayos 3c62613c30 clients/go: initial Go SDK for clawdforge
Idiomatic Go client wrapping the FastAPI surface in server.py — Healthz,
Run, UploadFile/UploadReader, and admin token CRUD. stdlib net/http only,
context-first signatures, typed errors (ErrAuth sentinel, RunFailure for
/run 502s, APIError for other 4xx/5xx, TransportError for network/EOF).

RunResult.Result is captured as json.RawMessage and materialized via
.AsJSON(out) / .AsText() because claude returns either parsed JSON or
plain text depending on prompt. UploadFile streams via io.Pipe + multipart
without buffering the file in memory.

Module: gitea.sulkta.com/Sulkta-Coop/clawdforge/clients/go
Includes cmd/cf-cli demo binary and httptest-based test suite (13 tests).
2026-04-28 22:36:56 -07:00

62 lines
2 KiB
Go

package clawdforge
import (
"errors"
"fmt"
)
// ErrAuth is the sentinel returned for 401/403 responses from clawdforge.
// Use errors.Is(err, ErrAuth) to detect auth failures.
var ErrAuth = errors.New("clawdforge: authentication failed")
// APIError carries a non-2xx response from clawdforge — status code plus
// the response body (truncated to a sane size). It is returned for any
// HTTP status >= 400 that isn't an auth failure.
type APIError struct {
StatusCode int
// Body is the raw response body (truncated). Useful for debugging.
Body string
// Message is a short human-readable summary derived from the body
// when the body is JSON of the form {"error": "..."} or {"detail": "..."}.
Message string
}
func (e *APIError) Error() string {
if e.Message != "" {
return fmt.Sprintf("clawdforge: HTTP %d: %s", e.StatusCode, e.Message)
}
return fmt.Sprintf("clawdforge: HTTP %d: %s", e.StatusCode, e.Body)
}
// TransportError wraps a low-level network/transport failure (DNS, connect
// refused, TLS, EOF, context cancellation, etc.). The wrapped error is
// available via errors.Unwrap.
type TransportError struct {
Op string
Err error
}
func (e *TransportError) Error() string {
return fmt.Sprintf("clawdforge: transport %s: %v", e.Op, e.Err)
}
func (e *TransportError) Unwrap() error { return e.Err }
// RunFailure represents a 502 response from /run — clawdforge accepted the
// request but `claude -p` failed (timeout, non-zero exit, etc.). The body
// fields mirror the server's failure shape.
//
// RunFailure satisfies APIError-like semantics by also embedding the status
// code via the underlying APIError, but it's a distinct type so callers can
// branch on errors.As(err, &cf.RunFailure{}).
type RunFailure struct {
StatusCode int
Err string `json:"error"`
Stderr string `json:"stderr"`
DurationMS int `json:"duration_ms"`
StopReason string `json:"stop_reason"`
}
func (e *RunFailure) Error() string {
return fmt.Sprintf("clawdforge: run failed (stop_reason=%s): %s", e.StopReason, e.Err)
}