Documentation
¶
Overview ¶
Package errs is the public error-contract surface for lark-cli.
It defines a closed taxonomy (9 Categories) and a small set of typed errors that embed Problem — an RFC 7807-aligned shared shape. External consumers (AI agents, shell scripts, integrating SDKs) read structured fields instead of regex-parsing free-string error messages.
The Problem shape ¶
Every typed error embeds Problem so the JSON wire shape (`type`, `subtype`, `code`, `message`, `hint`, `log_id`, `retryable`) is uniform across categories. Typed extensions (PermissionError.MissingScopes, SecurityPolicyError.ChallengeURL, etc.) appear at the top level of the envelope alongside the shared fields, not nested under a `detail` key.
Working with typed errors ¶
Use ProblemOf to read shared fields polymorphically:
if p, ok := errs.ProblemOf(err); ok {
log.Printf("category=%s subtype=%s retryable=%t", p.Category, p.Subtype, p.Retryable)
}
Use the IsXxx predicates or stdlib errors.As to branch on concrete type:
if errs.IsPermission(err) {
var pe *errs.PermissionError
_ = errors.As(err, &pe)
fmt.Println("missing scopes:", pe.MissingScopes)
}
Use WrapInternal at boundaries to lift any non-typed error to *InternalError; typed errors pass through unchanged.
Index ¶
- func IsAPI(err error) bool
- func IsAuthentication(err error) bool
- func IsConfig(err error) bool
- func IsConfirmationRequired(err error) bool
- func IsContentSafety(err error) bool
- func IsInternal(err error) bool
- func IsNetwork(err error) bool
- func IsPermission(err error) bool
- func IsRetryable(err error) bool
- func IsSecurityPolicy(err error) bool
- func IsValidation(err error) bool
- func UnwrapTypedError(err error) (error, bool)
- func WrapInternal(err error) error
- type APIError
- type AuthenticationError
- type Category
- type ConfigError
- type ConfirmationRequiredError
- type ContentSafetyError
- type InternalError
- type NetworkError
- type PermissionError
- type Problem
- type SecurityPolicyError
- type Subtype
- type ValidationError
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func IsAuthentication ¶
IsAuthentication reports whether err is an *AuthenticationError.
func IsConfirmationRequired ¶
IsConfirmationRequired reports whether err is a *ConfirmationRequiredError.
func IsContentSafety ¶
IsContentSafety reports whether err is a *ContentSafetyError.
func IsInternal ¶
IsInternal reports whether err is an *InternalError.
func IsPermission ¶
IsPermission reports whether err is a *PermissionError.
func IsRetryable ¶
IsRetryable reads Problem.Retryable; non-typed errors are non-retryable by default.
func IsSecurityPolicy ¶
IsSecurityPolicy reports whether err is a *SecurityPolicyError.
func IsValidation ¶
IsValidation reports whether err is a *ValidationError.
func UnwrapTypedError ¶
UnwrapTypedError walks the wrap chain and returns the first error that embeds Problem (i.e. any typed error in this package). Returns the typed error itself (as error) so callers — notably JSON marshaling — see the concrete value's own struct tags rather than an opaque wrapper.
func WrapInternal ¶
WrapInternal wraps a non-typed error into *InternalError. Typed errors (anything implementing problemCarrier) pass through unchanged. Component is metric-only and derived by the dispatcher, so it is not a parameter here.
Types ¶
type APIError ¶
APIError is the typed error for CategoryAPI (catch-all for classified Lark API business errors). Detail preserves the raw Lark error map for diagnostics.
type AuthenticationError ¶
type AuthenticationError struct {
Problem
UserOpenID string `json:"user_open_id,omitempty"`
Cause error `json:"-"`
}
AuthenticationError is the typed error for CategoryAuthentication. Cause preserves an optional wrapped sentinel for errors.Is / errors.Unwrap; it is intentionally not serialized.
func (*AuthenticationError) Unwrap ¶
func (e *AuthenticationError) Unwrap() error
Unwrap is nil-receiver safe; see ValidationError.Unwrap.
type Category ¶
type Category string
Category is the top-level taxonomy axis. Wire JSON: "type".
const ( CategoryValidation Category = "validation" CategoryAuthentication Category = "authentication" CategoryAuthorization Category = "authorization" CategoryConfig Category = "config" CategoryNetwork Category = "network" CategoryAPI Category = "api" CategoryPolicy Category = "policy" CategoryInternal Category = "internal" CategoryConfirmation Category = "confirmation" )
func CategoryOf ¶
CategoryOf returns the error's Category for metrics/logging/dispatch routing. Falls back to CategoryInternal for non-typed errors.
type ConfigError ¶
ConfigError is the typed error for CategoryConfig. Cause preserves an optional wrapped sentinel for errors.Is / errors.Unwrap; it is intentionally not serialized.
func (*ConfigError) Unwrap ¶
func (e *ConfigError) Unwrap() error
Unwrap is nil-receiver safe; see ValidationError.Unwrap.
type ConfirmationRequiredError ¶
type ConfirmationRequiredError struct {
Problem
Risk string `json:"risk"`
Action string `json:"action"`
}
ConfirmationRequiredError is the typed error for CategoryConfirmation. Risk is one of: "read" | "write" | "high-risk-write".
type ContentSafetyError ¶
ContentSafetyError is the typed error for CategoryPolicy content-safety subtypes.
type InternalError ¶
InternalError is the typed error for CategoryInternal. Cause is preserved for logging but not emitted on the wire.
func (*InternalError) Unwrap ¶
func (e *InternalError) Unwrap() error
Unwrap is nil-receiver safe; see ValidationError.Unwrap.
type NetworkError ¶
type NetworkError struct {
Problem
CauseKind string `json:"cause,omitempty"`
Cause error `json:"-"`
}
NetworkError is the typed error for CategoryNetwork. CauseKind (string) is one of: "timeout" | "tls" | "dns" | "5xx" — the canonical wire taxonomy (emitted as JSON key "cause"). Cause preserves an optional wrapped sentinel for errors.Is / errors.Unwrap; it is intentionally not serialized.
func (*NetworkError) Unwrap ¶
func (e *NetworkError) Unwrap() error
Unwrap is nil-receiver safe; see ValidationError.Unwrap.
type PermissionError ¶
type PermissionError struct {
Problem
MissingScopes []string `json:"missing_scopes,omitempty"`
Identity string `json:"identity,omitempty"`
ConsoleURL string `json:"console_url,omitempty"`
}
PermissionError is the typed error for CategoryAuthorization.
type Problem ¶
type Problem struct {
Category Category `json:"type"`
Subtype Subtype `json:"subtype,omitempty"`
Code int `json:"code,omitempty"`
Message string `json:"message"`
Hint string `json:"hint,omitempty"`
LogID string `json:"log_id,omitempty"`
Retryable bool `json:"retryable,omitempty"`
}
Problem is the RFC 7807-aligned shared shape embedded by every typed error.
Message is REQUIRED. Producers must populate it; an empty Message will make Error() return "" — a known Go footgun for fmt.Errorf("...: %v", err).
Wire-format notes:
- No Component field. Service / shortcut component is metric-only enrichment derived by the dispatcher from the cobra command path; it never appears on the wire.
- No DocURL field. PermissionError carries the same intent via its typed ConsoleURL extension; other typed errors do not link out.
- Retryable uses omitempty so only `true` is emitted; consumers treat absence as false.
func ProblemOf ¶
ProblemOf extracts the embedded Problem via the non-exported problemCarrier interface. This is the supported way to read shared fields without depending on a specific typed error.
A typed error whose embedded *Problem is nil is treated as "not a problem carrier" — returning (nil, true) here would cause CategoryOf / IsRetryable and other downstream readers to dereference nil.
func (*Problem) Error ¶
Error satisfies the standard `error` interface. A nil receiver is treated as the empty string so a stray nil *Problem stored in an error interface cannot panic the dispatcher.
func (*Problem) ProblemDetail ¶
type SecurityPolicyError ¶
type SecurityPolicyError struct {
Problem
ChallengeURL string `json:"challenge_url,omitempty"`
Cause error `json:"-"`
}
SecurityPolicyError is the typed error for CategoryPolicy security-policy subtypes. Subtype is "challenge_required" or "access_denied"; Code is 21000 or 21001.
func (*SecurityPolicyError) Unwrap ¶
func (e *SecurityPolicyError) Unwrap() error
Unwrap is nil-receiver safe; see ValidationError.Unwrap.
type Subtype ¶
type Subtype string
Subtype is the second-level taxonomy axis. Wire JSON: "subtype".
const ( SubtypeTokenMissing Subtype = "token_missing" // no token in request (Authorization header absent / no local token cache) SubtypeTokenInvalid Subtype = "token_invalid" // token present but content/format wrong SubtypeTokenExpired Subtype = "token_expired" // token explicitly expired SubtypeRefreshTokenInvalid Subtype = "refresh_token_invalid" // refresh_token is v1 legacy format, unusable SubtypeRefreshTokenExpired Subtype = "refresh_token_expired" // refresh_token expired SubtypeRefreshTokenRevoked Subtype = "refresh_token_revoked" // refresh_token revoked (user logout / admin action) SubtypeRefreshTokenReused Subtype = "refresh_token_reused" // refresh_token already used (single-use rotation triggered) SubtypeRefreshServerError Subtype = "refresh_server_error" // refresh endpoint transient error (retryable) )
CategoryAuthentication subtypes
const ( SubtypeMissingScope Subtype = "missing_scope" // user authorized app but did not grant this scope SubtypeAppScopeNotApplied Subtype = "app_scope_not_applied" // app did not apply for this scope on the open platform SubtypeTokenScopeInsufficient Subtype = "token_scope_insufficient" // token was issued without this scope (RFC 6750 alignment) SubtypeAppNotInstalled Subtype = "app_not_installed" // app not enabled / not installed in this tenant )
CategoryAuthorization subtypes
const ( SubtypeInvalidClient Subtype = "invalid_client" // app_id / app_secret incorrect (RFC 6749 §5.2 alignment) SubtypeNotConfigured Subtype = "not_configured" // local config file absent (user has not run `config init`) SubtypeInvalidConfig Subtype = "invalid_config" // local config file present but malformed )
CategoryConfig subtypes
const ( SubtypeRateLimit Subtype = "rate_limit" // request rate limit exceeded SubtypeConflict Subtype = "conflict" // resource state conflict (e.g. concurrent modification) SubtypeCrossTenant Subtype = "cross_tenant" // operation crosses tenant boundary (not supported) SubtypeCrossBrand Subtype = "cross_brand" // operation crosses brand boundary (feishu vs lark, not supported) SubtypeInvalidParameters Subtype = "invalid_parameters" // API-side parameter validation rejected the request SubtypeOwnershipMismatch Subtype = "ownership_mismatch" // caller is not the resource owner )
CategoryAPI subtypes
const ( SubtypeChallengeRequired Subtype = "challenge_required" // user must complete browser challenge / MFA SubtypeAccessDenied Subtype = "access_denied" // policy denies access outright )
CategoryPolicy subtypes (security-policy envelope shape)
const ( SubtypeSDKError Subtype = "sdk_error" // lark SDK Do() returned an unexpected error SubtypeInvalidResponse Subtype = "invalid_response" // SDK response body not parsable as JSON )
CategoryInternal subtypes
const ( SubtypeTaskInvalidParams Subtype = "task_invalid_params" SubtypeTaskPermissionDenied Subtype = "task_permission_denied" SubtypeTaskNotFound Subtype = "task_not_found" SubtypeTaskConflict Subtype = "task_conflict" SubtypeTaskServerError Subtype = "task_server_error" SubtypeTaskAssigneeLimit Subtype = "task_assignee_limit" SubtypeTaskFollowerLimit Subtype = "task_follower_limit" SubtypeTaskTasklistMemberLimit Subtype = "task_tasklist_member_limit" SubtypeTaskReminderExists Subtype = "task_reminder_exists" )
Task service subtypes — consumed by internal/errclass/codemeta_task.go.
const (
SubtypeInvalidArgument Subtype = "invalid_argument" // user-supplied flag / arg failed validation (gRPC INVALID_ARGUMENT alignment)
)
CategoryValidation subtypes
const (
SubtypeNetworkTransport Subtype = "transport" // transport-layer failure (timeout / TLS / DNS / 5xx); see NetworkError.CauseKind
)
CategoryNetwork subtypes
const (
SubtypeUnknown Subtype = "unknown" // catch-all fallback; producers must prefer a specific subtype
)
type ValidationError ¶
type ValidationError struct {
Problem
Param string `json:"param,omitempty"`
Cause error `json:"-"`
}
ValidationError is the typed error for CategoryValidation. Cause preserves an optional wrapped sentinel for errors.Is / errors.Unwrap; it is intentionally not serialized.
func (*ValidationError) Unwrap ¶
func (e *ValidationError) Unwrap() error
Unwrap exposes the wrapped cause so errors.Unwrap / errors.Is can traverse it. A nil typed-pointer held inside an error interface is treated as "no cause" so callers cannot panic on `errors.Unwrap(err)`.