Documentation
¶
Overview ¶
Package apierr is PayCLI's error vocabulary (§11): a closed set of stable machine-readable codes, a total code-to-exit-status map, and the normaliser that folds Payload's six different error body shapes into one.
Nothing in this package reads a human-readable server message to decide a code. Payload runs followingFieldsInvalid, noFilesUploaded, notAllowedToPerformAction and deletedCountSuccessfully through req.t, so their text changes with the project's i18n config, a payload-lng cookie or a proxy-injected Accept-Language. Only errors[].name, the JSON structure and the HTTP status are trusted. The two strings Payload hardcodes in English — `Route not found "…"` and `Cannot <METHOD> …` — are the sole exceptions and are named explicitly at each use.
Index ¶
- Constants
- func AnySent(fields []Field) bool
- func CodesByExit() map[int][]Code
- func DefaultHint(code Code) string
- func DidYouMean(input string, candidates []string) []string
- func ExitCode(err error) int
- func HasCode(err error, code Code) bool
- func HumanLine(e *Error) string
- type Cause
- type Code
- type Confidence
- type Error
- func As(err error) (*Error, bool)
- func CheckID(collection, id, idType string) *Error
- func CheckVersions(collection string, hasVersions *bool) *Error
- func Classify(r Response) *Error
- func From(err error) *Error
- func Network(err error) *Error
- func New(code Code, format string, args ...any) *Error
- func Wrap(err error, code Code, format string, args ...any) *Error
- func (e *Error) Error() string
- func (e *Error) Is(target error) bool
- func (e *Error) Recode(code Code) *Error
- func (e *Error) Unwrap() error
- func (e *Error) WithCauses(causes ...Cause) *Error
- func (e *Error) WithConfidence(c Confidence) *Error
- func (e *Error) WithDidYouMean(suggestions ...string) *Error
- func (e *Error) WithFailures(failures ...Failure) *Error
- func (e *Error) WithFields(fields ...Field) *Error
- func (e *Error) WithHTTP(h *HTTP) *Error
- func (e *Error) WithHint(format string, args ...any) *Error
- func (e *Error) WithRaw(body []byte, noRedact bool) *Error
- func (e *Error) WithWrapped(err error) *Error
- type Failure
- type Field
- type HTTP
- type Relationship
- type Response
Constants ¶
const ( IDTypeNumber = "number" IDTypeString = "string" IDTypeUnknown = "unknown" )
ID types a collection can have (§7.6). "unknown" is a real, common value and it disables every local check that depends on the id type: a client-side rejection built on a fact PayCLI never learned is a fabricated error.
const ( CauseHigh = "high" CauseMedium = "medium" CauseLow = "low" )
Cause confidence levels.
const ( ExitOK = 0 ExitInternal = 1 ExitAuth = 2 ExitThrottled = 3 ExitNotFound = 4 ExitValidation = 5 ExitNetwork = 6 ExitPartial = 7 ExitAccessDenied = 8 ExitConfig = 9 ExitCapability = 10 ExitConfirmationRequired = 11 )
Process exit statuses (§11.4). 125–128 and 130 are shell/signal territory and are never used.
const ( AuthModeAPIKey = "api-key" AuthModeJWT = "jwt" AuthModeAnonymous = "anonymous" )
Auth modes (§5.0). Duplicated as plain strings rather than imported so that apierr stays free of dependencies on config/secret.
const DefaultDocs = "pay explain --section exit_codes"
DefaultDocs is error.docs on every error PayCLI produces.
const MaxExit = ExitConfirmationRequired
MaxExit is the largest status PayCLI ever returns.
Variables ¶
This section is empty.
Functions ¶
func CodesByExit ¶
CodesByExit groups the vocabulary by exit status, for help and explain output.
func DefaultHint ¶
DefaultHint returns the starting hint for a code.
func DidYouMean ¶
DidYouMean ranks candidates by closeness to input for error.did_you_mean. A prefix or substring match always beats a pure edit-distance match, because "page" -> "pages" is the mistake agents actually make.
func ExitCode ¶
ExitCode extracts the process exit status from any error: 0 for nil, the mapped status for an *Error anywhere in the chain, and ExitInternal for an error that never passed through this package.
Types ¶
type Cause ¶
type Cause struct {
Cause string `json:"cause"`
Confidence string `json:"confidence"`
Detail string `json:"detail,omitempty"`
Fix string `json:"fix,omitempty"`
}
Cause is one entry of error.likely_causes, used to diagnose opaque 500s (§11.3). Confidence is "high" | "medium" | "low".
func Causes500 ¶
func Causes500(method, path string, rels []Relationship) []Cause
Causes500 ranks the likely causes of an opaque 500 (§11.3). The first two rows of that table are rejected client-side before the call, so what remains is the relationship diagnosis plus the standing advice that the real message is one config flag away.
type Code ¶
type Code string
Code is a stable, machine-readable error identifier. Agents branch on it; it never changes for a given failure mode.
const ( CodeInternal Code = "internal" CodeUnknown Code = "unknown" CodeCacheCorrupt Code = "cache_corrupt" CodeUpdateVerificationFailed Code = "update_verification_failed" CodeAuditWriteFailed Code = "audit_write_failed" )
Exit 1 — generic / internal.
const ( CodeAuthMissing Code = "auth_missing" CodeAuthInvalid Code = "auth_invalid" CodeAuthRequired Code = "auth_required" CodeAuthLocked Code = "auth_locked" CodeAuthUnverifiedEmail Code = "auth_unverified_email" CodeAuthInsecurePermissions Code = "auth_insecure_permissions" CodeAuthHelperFailed Code = "auth_helper_failed" )
Exit 2 — auth: *your credentials* are the problem.
const ( CodeRateLimited Code = "rate_limited" CodeDocLocked Code = "doc_locked" CodeServerBusy Code = "server_busy" )
Exit 3 — throttled / temporarily unavailable. Auto-retried.
const ( CodeDocNotFound Code = "doc_not_found" CodeRouteNotFound Code = "route_not_found" CodeVersionNotFound Code = "version_not_found" )
Exit 4 — not found.
const ( CodeValidationFailed Code = "validation_failed" CodeQueryPathInvalid Code = "query_path_invalid" CodeInvalidArgs Code = "invalid_args" CodeInvalidWhereSyntax Code = "invalid_where_syntax" CodeInvalidSortField Code = "invalid_sort_field" CodeUnknownField Code = "unknown_field" CodeInvalidOption Code = "invalid_option" CodeInvalidID Code = "invalid_id" CodeBadRequestBody Code = "bad_request_body" CodeWhereRequired Code = "where_required" CodeFileMissing Code = "file_missing" CodeNotUploadCollection Code = "not_upload_collection" CodeUnsupportedOperator Code = "unsupported_operator" CodeBulkLimitExceeded Code = "bulk_limit_exceeded" CodeRequestTooLarge Code = "request_too_large" CodeFormatUnsupported Code = "format_unsupported" CodeInvalidPathExpr Code = "invalid_path_expr" )
Exit 5 — validation / bad input.
const ( CodeNetworkUnreachable Code = "network_unreachable" CodeDNSFailure Code = "dns_failure" CodeTLSError Code = "tls_error" CodeTimeout Code = "timeout" CodeServerError Code = "server_error" CodeNonJSONResponse Code = "non_json_response" )
Exit 6 — network / server.
const ( CodeAccessDenied Code = "access_denied" CodeAdminAccessDenied Code = "admin_access_denied" )
Exit 8 — access denied: identity valid, permission not.
const ( CodeConfigMissing Code = "config_missing" CodeConfigSecretInPlaintext Code = "config_secret_in_plaintext" CodeProfileUnknown Code = "profile_unknown" CodeBaseURLInvalid Code = "base_url_invalid" CodeEndpointNotPayload Code = "endpoint_not_payload" CodeAuthCollectionUnknown Code = "auth_collection_unknown" )
Exit 9 — config / profile.
const ( CodeCollectionUnknown Code = "collection_unknown" CodeGlobalUnknown Code = "global_unknown" CodeOperationUnsupported Code = "operation_unsupported" CodeDiscoveryFailed Code = "discovery_failed" CodeGraphQLDisabled Code = "graphql_disabled" CodeSchemaStale Code = "schema_stale" )
Exit 10 — capability / discovery.
const (
CodeConfirmationRequired Code = "confirmation_required"
)
Exit 11 — confirmation required.
const (
CodePartialFailure Code = "partial_failure"
)
Exit 7 — partial failure. NEVER auto-retried: some documents committed.
func Codes ¶
func Codes() []Code
Codes returns every declared code, sorted, for `pay explain --section exit_codes` and for tests.
func (Code) Exit ¶
Exit returns the process exit status for a code. An unregistered code is a programming error rather than a runtime condition; it maps to ExitInternal and the totality test is what prevents it from ever happening.
type Confidence ¶
type Confidence string
Confidence says how the code was decided (§11.1). It is always present.
certain — the classification rests on errors[].name, JSON structure or an
HTTP status: signals the project's i18n config cannot change.
probable — it rests on a structural heuristic and the confirming English
string was absent. §11.5's file_missing row is the only one.
const ( ConfidenceCertain Confidence = "certain" ConfidenceProbable Confidence = "probable" )
type Error ¶
type Error struct {
Code Code `json:"code"`
Exit int `json:"exit"`
Message string `json:"message"`
Hint string `json:"hint,omitempty"`
Retriable bool `json:"retriable"`
Confidence Confidence `json:"confidence"`
Fields []Field `json:"fields"`
Failures []Failure `json:"failures,omitempty"`
LikelyCauses []Cause `json:"likely_causes,omitempty"`
DidYouMean []string `json:"did_you_mean"`
Docs string `json:"docs,omitempty"`
HTTP *HTTP `json:"http,omitempty"`
Raw json.RawMessage `json:"raw,omitempty"`
// RawRedactedPaths is non-empty when redaction modified Raw. The CLI turns
// it into the warnings[] entry {"code":"raw_redacted","paths":[…]} so the
// agent knows the bytes differ from the wire (§11.1).
RawRedactedPaths []string `json:"-"`
// contains filtered or unexported fields
}
Error is the error half of the envelope (§11.1). Field order is the JSON key order: §11.1's listing, with failures and likely_causes inserted after fields (§12.5 and §11.3 add them to the same object and the two listings disagree on their position; fields-then-failures keeps all the per-item detail together).
func CheckID ¶
CheckID pre-empts §11.3's first opaque-500 cause: an id that does not parse as the collection's id type produces HTTP 500 "Something went wrong.", and on /duplicate it can produce an unintended write. It returns nil — meaning "send the request" — whenever idType is unknown or empty, per §9.3's tri-state rule.
func CheckVersions ¶
CheckVersions pre-empts §11.3's second opaque-500 cause: /versions on a collection without versions enabled. hasVersions is a tri-state — nil means "not discovered", and PayCLI then sends the request rather than guessing.
func Classify ¶
Classify folds any Payload error response into one *Error (§11.2, §11.5). It never panics: a project author can make formatErrors return literally anything, and afterError hooks can rewrite both body and status.
func From ¶
From coerces any error into an *Error so the envelope always has one. A nil error yields nil.
func Network ¶
Network classifies a transport-level failure (§11.4 exit 6). It is here rather than in internal/payload so the classification is testable without a socket and so every caller agrees on the code.
func New ¶
New builds an error from a code and a message. The exit status, retriability and the default hint all follow from the code, so no caller can invent a mapping. The message is scrubbed: server messages routinely embed URLs, and a URL can carry a credential.
func Wrap ¶
Wrap attaches a code and message to an existing error, preserving the chain for errors.Is / errors.As.
func (*Error) Recode ¶
Recode changes the code, keeping everything else, and re-derives the exit status and retriability. It is how a layer with more context sharpens a classification — the normaliser cannot tell route_not_found from collection_unknown without the manifest, for example.
func (*Error) WithCauses ¶
WithCauses sets error.likely_causes (§11.3).
func (*Error) WithConfidence ¶
func (e *Error) WithConfidence(c Confidence) *Error
WithConfidence overrides the default ConfidenceCertain.
func (*Error) WithDidYouMean ¶
WithDidYouMean sets error.did_you_mean.
func (*Error) WithFailures ¶
WithFailures sets error.failures for a partial_failure (§12.5).
func (*Error) WithFields ¶
WithFields sets error.fields, sorted sent-first (§11.1).
func (*Error) WithHTTP ¶
WithHTTP records the wire context, redacting the URL (§5.3 makes redaction of error.http.url mandatory).
func (*Error) WithHint ¶
WithHint replaces the default hint. Every "you cannot X" must be followed by "do Y instead", so a hint is effectively mandatory.
func (*Error) WithRaw ¶
WithRaw attaches the server's body after redaction. When redaction changed the bytes, RawRedactedPaths names what moved so the caller can emit the raw_redacted warning. noRedact reproduces the true bytes for --no-redact.
func (*Error) WithWrapped ¶
WithWrapped attaches an underlying Go error after construction.
type Failure ¶
type Failure struct {
ID any `json:"id"`
Code Code `json:"code"`
Message string `json:"message"`
Fields []Field `json:"fields"`
}
Failure is one entry of error.failures on a partial_failure (§12.5).
type Field ¶
type Field struct {
Path string `json:"path"`
Label string `json:"label,omitempty"`
Message string `json:"message"`
// Sent is true iff Path is a leaf of the request body PayCLI actually sent.
// It exists because Payload validates the WHOLE document on PATCH and
// therefore reports fields the caller never touched.
Sent bool `json:"sent"`
}
Field is one entry of error.fields.
func SortFields ¶
SortFields orders entries sent-first while preserving the server's order inside each group (§11.1).
type HTTP ¶
type HTTP struct {
Status int `json:"status"`
Method string `json:"method"`
URL string `json:"url"`
// PayloadErrorName is errors[0].name verbatim — the signal the normaliser
// classified on, exposed so an agent can audit the decision.
PayloadErrorName string `json:"payload_error_name,omitempty"`
Attempts int `json:"attempts"`
RetryAfter string `json:"retry_after,omitempty"`
// BodyExcerpt is at most excerptLimit characters of a non-JSON body.
BodyExcerpt string `json:"body_excerpt,omitempty"`
}
HTTP is error.http: what was on the wire when the failure happened.
type Relationship ¶
type Relationship struct {
Path string // request-body leaf path, e.g. "heroImage"
Collection string // the relationTo target slug
ID any // the id that was sent
}
Relationship describes one relationship value in a request body, used to diagnose §11.3's third cause after the fact.
type Response ¶
type Response struct {
Status int
Method string
URL string
ContentType string
Body []byte
Attempts int
RetryAfter string
// AuthMode and IdentityVerified drive the 403 split. The server cannot make
// this call for us: a 403 body is byte-identical whether the caller is
// unauthenticated or merely unpermitted (verified), and its message is
// translated on top of that.
AuthMode string
IdentityVerified bool
// UploadCollection is the target collection's flags.upload. A 400 against an
// upload collection with no errors[0].name is file_missing — a structural,
// language-independent signal (the English "No files were uploaded." is
// only a confirming signal that raises confidence).
UploadCollection bool
// Collection is the target slug, used in messages only.
Collection string
// SentPaths is the leaf-path set of the request body PayCLI actually sent,
// which decides Field.Sent and therefore which validation hint is used.
SentPaths map[string]bool
// IncludeRaw is false for --no-raw; NoRedact is true for --no-redact.
IncludeRaw bool
NoRedact bool
}
Response is everything the normaliser is allowed to look at. It is a plain struct rather than an *http.Response so classification is a pure function and every row of §11.2 and §11.5 is a table test.