mirror of
https://github.com/larksuite/cli.git
synced 2026-07-07 00:47:09 +08:00
Drive-domain errors now leave the CLI as typed, machine-branchable envelopes — a stable `type` plus `subtype` and named fields (param, params, retryable, log_id, hint) — so scripts and AI agents can branch on structure and act on a recovery hint instead of parsing prose. Changes: - Every error produced in the drive domain — validation, file I/O, and the failures returned from its Lark API calls — is emitted as a typed errs.* error; the exit code is derived from the error category. Drive's API calls now go through a shared typed classifier, so failures carry subtype, troubleshooter, a recovery hint, and the request's log_id whether the server returns it in the response body or the x-tt-logid header; an already-typed network/auth error is never downgraded into a generic API error. - Known API conditions (resource conflict, cross-tenant, cross-brand, ...) carry a recovery hint keyed by their error class; a command can refine that hint with command-specific guidance. - Batch partial failures (+push / +pull / +sync, where some items succeed and some fail) now report an honest ok:false multi-status result on stdout — the summary and every per-item outcome stay machine-readable — and exit non-zero, instead of a misleading ok:true success envelope. - Duplicate rel_path conflicts report each colliding path as a structured params entry (RFC 7807 invalid-params style). - Static guards lock the drive path so legacy error construction — direct envelopes or the auto-classifying API helpers — cannot be reintroduced, making drive the template for the remaining domains. Output changes worth noting for consumers: - Error envelopes now carry typed type/subtype and named fields; exit codes follow the error category (malformed or incomplete API responses are reported as internal errors rather than generic API errors). - Batch partial failures (+push / +pull / +sync) emit an ok:false result envelope on stdout (summary + per-item items[]) and exit non-zero; the per-item results stay on stdout rather than in a stderr error envelope. Errors surfaced through shared cross-domain helpers (scope precheck, media import upload, metadata lookup, save-path resolution) are not yet typed; they migrate with the shared layer in a follow-up change.
91 lines
5.4 KiB
Go
91 lines
5.4 KiB
Go
// Copyright (c) 2026 Lark Technologies Pte. Ltd.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package errs
|
|
|
|
// Subtype is the second-level taxonomy axis. Wire JSON: "subtype".
|
|
type Subtype string
|
|
|
|
const (
|
|
SubtypeUnknown Subtype = "unknown" // catch-all fallback; producers must prefer a specific subtype
|
|
)
|
|
|
|
// CategoryValidation subtypes
|
|
const (
|
|
SubtypeInvalidArgument Subtype = "invalid_argument" // user-supplied flag / arg failed validation (gRPC INVALID_ARGUMENT alignment)
|
|
SubtypeFailedPrecondition Subtype = "failed_precondition" // request is valid but the system/resource state is not in the state required to execute; caller must change state (not retry) — e.g. ambiguous remote mapping (gRPC FAILED_PRECONDITION alignment)
|
|
)
|
|
|
|
// CategoryAuthentication subtypes
|
|
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)
|
|
)
|
|
|
|
// CategoryAuthorization subtypes
|
|
const (
|
|
SubtypeMissingScope Subtype = "missing_scope" // user authorized app but did not grant this scope
|
|
SubtypeUserUnauthorized Subtype = "user_unauthorized" // user never authorized the app
|
|
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)
|
|
SubtypeAppUnavailable Subtype = "app_unavailable" // app status unavailable
|
|
SubtypeAppDisabled Subtype = "app_disabled" // app currently disabled in this tenant (was installed/enabled before)
|
|
SubtypePermissionDenied Subtype = "permission_denied" // resource-level permission denial (authenticated but lacks rights for this resource, HTTP 403 / gRPC PERMISSION_DENIED alignment)
|
|
)
|
|
|
|
// CategoryConfig 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
|
|
)
|
|
|
|
// CategoryNetwork subtypes
|
|
const (
|
|
SubtypeNetworkTransport Subtype = "transport" // fallback when no more-specific network subtype matches
|
|
SubtypeNetworkTimeout Subtype = "timeout" // dial / read timeout
|
|
SubtypeNetworkTLS Subtype = "tls" // TLS handshake / cert failure
|
|
SubtypeNetworkDNS Subtype = "dns" // DNS resolution failure
|
|
SubtypeNetworkServer Subtype = "server_error" // upstream HTTP 5xx
|
|
)
|
|
|
|
// CategoryAPI 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
|
|
SubtypeNotFound Subtype = "not_found" // referenced resource does not exist (HTTP 404 alignment)
|
|
SubtypeServerError Subtype = "server_error" // upstream server-side transient error (HTTP 5xx alignment, retryable)
|
|
SubtypeQuotaExceeded Subtype = "quota_exceeded" // resource quota / collection size limit reached (assignees, followers, members, etc.)
|
|
SubtypeAlreadyExists Subtype = "already_exists" // idempotency violation: resource already exists in target state
|
|
)
|
|
|
|
// CategoryPolicy subtypes (security-policy envelope shape)
|
|
const (
|
|
SubtypeChallengeRequired Subtype = "challenge_required" // user must complete browser challenge / MFA
|
|
SubtypeAccessDenied Subtype = "access_denied" // policy denies access outright
|
|
)
|
|
|
|
// CategoryInternal subtypes
|
|
const (
|
|
SubtypeSDKError Subtype = "sdk_error" // lark SDK Do() returned an unexpected error
|
|
SubtypeInvalidResponse Subtype = "invalid_response" // SDK response body not parsable as JSON
|
|
SubtypeFileIO Subtype = "file_io" // local file I/O failure (mkdir / write / read)
|
|
SubtypeStorage Subtype = "storage" // local persistence failure (e.g. config file save)
|
|
// Generic untyped error lifted to InternalError uses SubtypeUnknown.
|
|
)
|
|
|
|
// CategoryConfirmation subtypes
|
|
const (
|
|
SubtypeConfirmationRequired Subtype = "confirmation_required" // high-risk operation needs explicit --yes
|
|
)
|