Skip to content

feat(drive): emit typed error envelopes across the drive domain#1205

Merged
evandance merged 1 commit into
mainfrom
feat/errs-migrate-drive
Jun 3, 2026
Merged

feat(drive): emit typed error envelopes across the drive domain#1205
evandance merged 1 commit into
mainfrom
feat/errs-migrate-drive

Conversation

@evandance
Copy link
Copy Markdown
Collaborator

@evandance evandance commented Jun 1, 2026

Summary

Drive errors previously left the CLI as a free-form legacy envelope that callers had to parse by reading prose. This PR makes the drive domain's own error producers — validation, file I/O, and every Lark API call it makes — emit typed errs.* errors: a stable type + subtype plus named fields (param, params, retryable, hint, log_id), so scripts and AI agents can branch on structure and know the next action without string-matching. Two static guards (forbidigo + errscontract AST rules) lock the drive path so legacy shapes — direct envelopes and the auto-classifying API helpers — cannot be reintroduced. This is the reusable template for migrating the remaining business domains.

Errors that drive surfaces from shared cross-domain common helpers (scope precheck, media import upload, metadata lookup, save-path resolution) are explicitly out of scope here — they migrate with the shared layer in a follow-up (see "Out of scope / deferred").

What to focus on (reviewer guide)

  1. Consumer-visible behavior changes (see "Output changes" below) — envelope shape, two exit-code corrections, and the batch partial-failure result.
  2. The API-boundary migration (largest change): every drive Lark API call now goes through a new shared runtime.CallAPITyped (shortcuts/common/runner.go; drive calls it directly). It performs the same SDK request as runtime.CallAPI (identical transport + query model) but classifies failures via errclass.BuildAPIError into typed errors, and lifts log_id from both the response body and the x-tt-logid header (the body-only parse used to drop the header log_id — see review P1). Typed boundary errors are propagated unchanged — an already-typed network/auth error keeps its class and is never downgraded into a blanket api_error. This replaces 34 CallAPI + 1 DoAPIJSON sites that previously surfaced legacy api_error envelopes via the shared HandleApiResult. CallAPITyped is the reusable primitive the remaining domains adopt (so they need no per-domain wrapper). The classification lives in a shared ClassifyAPIResponse reused by both CallAPITyped and the file-upload DoAPI paths (so uploads also lift header log_id), and a non-JSON HTTP 5xx (e.g. a gateway 502 text/html) is classified as a retryable network/server_error instead of a mis-parsed internal error.
  3. Batch partial-failure outcome (new, addresses review): +push/+pull/+sync partial failures now emit an honest ok:false multi-status result on stdout (summary + per-item items[], both succeeded and failed) plus a typed *output.PartialFailureError exit signal — replacing the prior ok:true stdout envelope + ErrBare, which both lied about the ok field and used the predicate-only ErrBare for non-predicate commands. Modeled as a first-class third outcome in errs/ERROR_CONTRACT.md; ErrBare stays predicate-only.
  4. Classification decisions worth a sanity check:
    • Duplicate remote rel_path → validation / failed_precondition (gRPC FAILED_PRECONDITION: state not ready, fix state — not a bad arg, not a retry) + a recovery hint + structured params.
    • Malformed/incomplete API responses (success call, missing field) → internal / invalid_response (was a generic API error).
    • Per-subtype recovery hints for context-free API conditions (conflict, cross_tenant, cross_brand) live in errclass.APIHint(subtype) — sibling of the existing ConfigHint/PermissionHint. A command may refine the hint with command-specific guidance (e.g. +search names the offending --creator-ids/--sharer-ids); that command-level enrichment overrides the default.
  5. Anti-backslide lock — how legacy is mechanically prevented on shortcuts/drive/:
    • forbidigo bans output.Err*, fmt.Errorf, errors.New, and the shared legacy helpers (common.FlagErrorf / WrapInputStatError / WrapSaveErrorByCategory).
    • errscontract AST rules ban (a) &output.ExitError{} / &output.ErrDetail{} literals and (b) the auto-classifying runtime helpers runtime.CallAPI / DoAPIJSON / DoAPIJSONWithLogID on migrated paths.
    • output.ErrBare (predicate exit signal) and runtime.DoAPI (raw response, caller classifies) remain allowed.
  6. Shared-contract additions (errs/, internal/errclass/, internal/output/, shortcuts/common/, all additive / non-breaking): errs.InvalidParam + ValidationError.Params (RFC 7807 invalid-params), errs.SubtypeFailedPrecondition, errclass.APIHint, output.PartialFailureError + runtime.OutPartialFailure, and runtime.CallAPITyped (the shared typed API primitive other domains will reuse).

Changes

  • Add the shared runtime.CallAPITyped (shortcuts/common/runner.go): same SDK request as CallAPI, typed classification via errclass.BuildAPIError, and log_id lifted from the response body or x-tt-logid header. Drive calls it directly; migrate all 34 runtime.CallAPI + the DoAPIJSONWithLogID site across the domain to it.
  • Add the batch partial-failure outcome: runtime.OutPartialFailure writes an ok:false multi-status result to stdout and returns the typed output.PartialFailureError exit signal; migrate +push/+pull/+sync to it (no more Out(...)+ErrBare). Document it as the third outcome in errs/ERROR_CONTRACT.md.
  • Migrate the remaining drive error producers (output.Err*, fmt.Errorf) to typed errs.*; exit codes derive from the error category — shortcuts/drive/*.go.
  • Consolidate drive error helpers in shortcuts/drive/drive_errors.go (wrapDriveNetworkErr, driveInputStatError, driveSaveError, appendDriveExportRecoveryHint) — all propagate already-typed errors and preserve the cause; replace drive's calls to shared legacy helpers.
  • Rewrite enrichDriveSearchError to decorate the typed Problem in place (was *output.ExitError).
  • Duplicate rel_path: typed ValidationError (failed_precondition) with params + recovery hint — shortcuts/drive/list_remote.go.
  • Add errs.InvalidParam + ValidationError.Params + SubtypeFailedPrecondition (errs/), errclass.APIHint(subtype) (internal/errclass/classify.go), output.PartialFailureError (internal/output/).
  • Register drive Lark error codes — internal/errclass/codemeta_drive.go.
  • Lock the drive path: forbidigo deny-list additions in .golangci.yml + errscontract rules rule_no_legacy_envelope_literal.go and rule_no_legacy_runtime_api_call.go.

Test Plan

  • local full gate: gofmt -l, go vet, go build ./..., golangci-lint (--new-from-rev, 0 issues), errscontract scan, unit tests (88 pkgs + lint module) — all green
  • new unit coverage: CallAPITyped (code≠0 → typed APIError; success → data; typed network/auth boundary passthrough not downgraded; log_id lifted from header-only and body; non-JSON 5xx → retryable network/server_error incl. empty Content-Type; non-object JSON []invalid_response); OutPartialFailure (ok:false stdout envelope carrying both succeeded+failed items + typed PartialFailureError); the reclassified API-error / partial-failure command tests now assert the typed envelope
  • acceptance-reviewer passed on the drive migration (real CLI, typed envelopes); the API-boundary + partial-failure changes are covered by the new unit tests + the full gate above
  • manual: lark drive +search --mine --creator-ids ou_x → typed validation (mutually-exclusive flags), exit 2
  • CI re-running on latest push; prior commit was green incl. live E2E (e2e-live)

Output changes for consumers (CHANGELOG-worthy)

  • Error envelopes now carry typed type/subtype + named fields (param/params/hint/retryable/log_id); exit codes follow the error category.
  • Malformed/incomplete API responses are reported as internal (exit 5) instead of a generic API error (exit 1).
  • Batch partial failures (+push/+pull/+sync) emit an ok:false result envelope on stdout (summary + per-item items[], both succeeded and failed) and exit non-zero — replacing the prior misleading ok:true stdout envelope. The per-item results stay on stdout rather than in a stderr error envelope.

Out of scope / deferred

  • Per-item structured error in batch partial-failure: items[].error is still a string (pre-existing on +push/+pull/+sync). The overall outcome is typed (ok:false multi-status), but a structured per-item problem object (so an agent can branch per item on subtype/retryable/missing_scopes) is a follow-up — it changes the item output shape and touches all item-collection sites.
  • Shared common helpers that drive still surfaces legacy through — migrated in the shared-layer phase, not drive-local (cross-domain; would otherwise be duplicated or lossily re-wrapped): EnsureScopescheckShortcutScopes (scope precheck), common.UploadDriveMediaAll/Multipart (+import), common.FetchDriveMetaTitle (+inspect), common.ResolveSavePath (bare fmt.Errorf).
  • Bare context cancellation/timeout (return ctx.Err()) at a few pre-existing sites still degrades to a plain-text root error; this is the cross-cutting untyped-passthrough class, fixed systematically by the planned root catch-all (not drive-local).
  • The shared legacy helpers themselves (FlagErrorf/Wrap*Error) stay for not-yet-migrated domains; typed/removed globally in the shared-layer phase. Drive no longer calls them.
  • Lint hardening follow-ups: resolve legacy construction via go/types to also catch alias / new() bypasses; converge the migrated-path list (.golangci.yml + errscontract) into a single manifest.

Related Issues

N/A

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jun 1, 2026

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds per-parameter diagnostics to errs.ValidationError, registers drive API code metadata, tightens forbidigo linting, adds drive I/O/error helpers and errclass classification, migrates many Drive shortcuts to typed errs (validation/internal/network/API), updates partial-failure stdout behavior, and updates tests.

Changes

Typed error infrastructure and drive shortcuts

Layer / File(s) Summary
Linter configuration
.golangci.yml
Expands forbidigo path scopes and adds forbidden patterns banning legacy helper usage and bare fmt.Errorf/errors.New on migrated paths.
Validation types and tests
errs/types.go, errs/types_test.go
Adds InvalidParam, ValidationError.Params []InvalidParam, and WithParams(...InvalidParam) plus tests validating append/JSON/chaining behavior.
errclass drive metadata
internal/errclass/codemeta_drive.go, internal/errclass/codemeta_drive_test.go
Registers drive service Lark error-code -> CodeMeta mappings and adds a table-driven test verifying LookupCodeMeta for those codes.
Drive +add-comment migration
shortcuts/drive/drive_add_comment.go, shortcuts/drive/drive_add_comment_test.go
Migrates +add-comment validation/runtime checks to typed errs with .WithParam(...) and WithHint(...); tests now assert typed ValidationError hints and param attribution.
Drive I/O helpers
shortcuts/drive/drive_io.go, shortcuts/drive/drive_io_test.go
Adds driveInputStatError and driveSaveError to map file/stat/save errors into typed errs and updates tests to assert typed validation errors with params.
Drive validation, helpers & API mapping
shortcuts/drive/*
Introduces wrapDriveNetworkErr, driveUploadClassifyContext, replaces legacy output helpers with typed errs across many shortcuts, maps non-zero API codes via errclass.BuildAPIError, and updates numerous command validations to use .WithParam(...).
duplicateRemotePath -> InvalidParam
shortcuts/drive/list_remote.go
Rewrites duplicate-remote error to return errs.NewValidationError populated with InvalidParam entries per colliding rel_path; replaces fmt.Errorf paths with typed InternalError.
Partial-failure stdout contract & tests
shortcuts/drive/drive_pull.go, shortcuts/drive/drive_sync.go, tests/cli_e2e/drive/*
Always emit partial-failure {summary, items} to stdout and return a bare ExitAPI; update tests/helpers to parse stdout JSON and assert typed errors.
Tests: assertions updated to typed errs
shortcuts/drive/*_test.go, tests/cli_e2e/drive/*
Many tests updated to use errors.As to assert *errs.ValidationError, *errs.InternalError, *errs.APIError, or *errs.PermissionError, and to check Subtype, Param, Params, Hint, and exit-code mappings.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • larksuite/cli#339: Related — introduces typed input-stat error handling and touches drive input stat wrappers referenced by this migration.
  • larksuite/cli#984: Related — extends typed errs contract and lint enforcement that this change tightens.
  • larksuite/cli#709: Related — overlaps Drive upload/push changes and errclass-based API error classification.

Suggested reviewers

  • liangshuo-1
  • fangshuyu-768

"I hop through code with gentle paws,
I bind params and mend bare flaws.
Lint keeps rhythm, typed errs sing,
Params in rows make tests take wing.
A happy rabbit hops — the CLI springs."

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/errs-migrate-drive

@github-actions github-actions Bot added domain/ccm PR touches the ccm domain size/L Large or sensitive change across domains or core paths labels Jun 1, 2026
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Jun 1, 2026

🚀 PR Preview Install Guide

🧰 CLI update

npm i -g https://pkg.pr.new/larksuite/cli/@larksuite/cli@1ee4cb2b5bb22bf834290c0f989dbfc6957761a2

🧩 Skill update

npx skills add larksuite/cli#feat/errs-migrate-drive -y -g

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
shortcuts/drive/drive_import_common.go (1)

225-240: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Bind --file/--type invalid-param metadata on file type mismatch

ValidationError.WithParams takes ...errs.InvalidParam (not strings). This error is currently the only one in validateDriveImportSpec that omits param binding; since the failure is the --type vs file-extension pairing, attach both parameters to keep the invalid-params envelope consistent with the sibling cases.

♻️ Proposed change
-		return errs.NewValidationError(errs.SubtypeInvalidArgument, "file type mismatch: %s", hint)
+		return errs.NewValidationError(errs.SubtypeInvalidArgument, "file type mismatch: %s", hint).
+			WithParams(
+				errs.InvalidParam{Name: "--file", Reason: hint},
+				errs.InvalidParam{Name: "--type", Reason: hint},
+			)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@shortcuts/drive/drive_import_common.go` around lines 225 - 240, The file-type
mismatch branch in validateDriveImportSpec returns a ValidationError without
binding InvalidParam metadata; change the error construction to use
ValidationError.WithParams (or the equivalent helper used elsewhere) and attach
two errs.InvalidParam entries for the offending flags (e.g. name="--file" with
value spec.Filename or ext, and name="--type" with value spec.DocType) so the
returned error matches the other cases that call errs.WithParams and provides
structured invalid-param info.
🧹 Nitpick comments (1)
shortcuts/drive/drive_search.go (1)

444-448: 💤 Low value

Optional: attribute the hard-cap span error to both --opened-* flags via WithParams.

This validation spans --opened-since and --opened-until, so a single WithParam doesn't fit, but the new multi-parameter Params support added in this stack would let agents branch on the offending flags here too. Leaving Param empty (as with the mutual-exclusion cases) stays consistent.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@shortcuts/drive/drive_search.go` around lines 444 - 448, The validation error
for the opened-window span should attribute the offending flags so agents can
branch on them: modify the returned error from errs.NewValidationError(...) in
drive_search.go to include the multi-parameter attribution (use the new
Params/WithParams API) listing both "--opened-since" and "--opened-until" (keep
the same error message and driveSearchMaxOpenedSpanDays reference).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@shortcuts/drive/drive_download.go`:
- Around line 58-60: The validation error returned when an existing output file
is detected should include the parameter binding like the others; update the
error construction in the FileIO stat check (the branch that uses
runtime.FileIO().Stat and the overwrite variable) to call .WithParam("--output")
on the errs.NewValidationError result so the returned error is consistent with
the validation errors earlier in this function.

---

Outside diff comments:
In `@shortcuts/drive/drive_import_common.go`:
- Around line 225-240: The file-type mismatch branch in validateDriveImportSpec
returns a ValidationError without binding InvalidParam metadata; change the
error construction to use ValidationError.WithParams (or the equivalent helper
used elsewhere) and attach two errs.InvalidParam entries for the offending flags
(e.g. name="--file" with value spec.Filename or ext, and name="--type" with
value spec.DocType) so the returned error matches the other cases that call
errs.WithParams and provides structured invalid-param info.

---

Nitpick comments:
In `@shortcuts/drive/drive_search.go`:
- Around line 444-448: The validation error for the opened-window span should
attribute the offending flags so agents can branch on them: modify the returned
error from errs.NewValidationError(...) in drive_search.go to include the
multi-parameter attribution (use the new Params/WithParams API) listing both
"--opened-since" and "--opened-until" (keep the same error message and
driveSearchMaxOpenedSpanDays reference).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: a0714d18-2c47-4e61-a1f5-615e7f537e1e

📥 Commits

Reviewing files that changed from the base of the PR and between 639259f and 3113cb2.

📒 Files selected for processing (39)
  • .golangci.yml
  • errs/types.go
  • errs/types_test.go
  • internal/errclass/codemeta_drive.go
  • internal/errclass/codemeta_drive_test.go
  • shortcuts/drive/drive_add_comment.go
  • shortcuts/drive/drive_add_comment_test.go
  • shortcuts/drive/drive_apply_permission.go
  • shortcuts/drive/drive_create_folder.go
  • shortcuts/drive/drive_create_shortcut.go
  • shortcuts/drive/drive_delete.go
  • shortcuts/drive/drive_download.go
  • shortcuts/drive/drive_duplicate_remote_test.go
  • shortcuts/drive/drive_export.go
  • shortcuts/drive/drive_export_common.go
  • shortcuts/drive/drive_export_download.go
  • shortcuts/drive/drive_export_test.go
  • shortcuts/drive/drive_import.go
  • shortcuts/drive/drive_import_common.go
  • shortcuts/drive/drive_inspect.go
  • shortcuts/drive/drive_move.go
  • shortcuts/drive/drive_move_common.go
  • shortcuts/drive/drive_pull.go
  • shortcuts/drive/drive_pull_test.go
  • shortcuts/drive/drive_push.go
  • shortcuts/drive/drive_search.go
  • shortcuts/drive/drive_search_test.go
  • shortcuts/drive/drive_secure_label.go
  • shortcuts/drive/drive_status.go
  • shortcuts/drive/drive_status_test.go
  • shortcuts/drive/drive_sync.go
  • shortcuts/drive/drive_sync_test.go
  • shortcuts/drive/drive_task_result.go
  • shortcuts/drive/drive_task_result_test.go
  • shortcuts/drive/drive_upload.go
  • shortcuts/drive/drive_upload_classify_test.go
  • shortcuts/drive/drive_version.go
  • shortcuts/drive/drive_version_test.go
  • shortcuts/drive/list_remote.go

Comment thread shortcuts/drive/drive_download.go
@codecov
Copy link
Copy Markdown

codecov Bot commented Jun 1, 2026

Codecov Report

❌ Patch coverage is 64.28571% with 170 lines in your changes missing coverage. Please review.
✅ Project coverage is 69.16%. Comparing base (0aa9e96) to head (1ee4cb2).

Files with missing lines Patch % Lines
shortcuts/drive/drive_errors.go 42.10% 17 Missing and 5 partials ⚠️
shortcuts/common/runner.go 68.18% 17 Missing and 4 partials ⚠️
shortcuts/drive/drive_push.go 45.45% 17 Missing and 1 partial ⚠️
shortcuts/drive/drive_add_comment.go 73.33% 16 Missing ⚠️
shortcuts/drive/drive_export_common.go 40.74% 16 Missing ⚠️
shortcuts/drive/drive_pull.go 50.00% 11 Missing ⚠️
shortcuts/drive/drive_import_common.go 50.00% 9 Missing ⚠️
shortcuts/drive/drive_task_result.go 60.00% 8 Missing ⚠️
shortcuts/drive/drive_version.go 42.85% 8 Missing ⚠️
internal/errclass/classify.go 40.00% 6 Missing ⚠️
... and 14 more
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1205      +/-   ##
==========================================
- Coverage   69.19%   69.16%   -0.04%     
==========================================
  Files         634      636       +2     
  Lines       59482    59546      +64     
==========================================
+ Hits        41161    41184      +23     
- Misses      15007    15041      +34     
- Partials     3314     3321       +7     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@evandance evandance force-pushed the feat/errs-migrate-drive branch 2 times, most recently from a3361c0 to 37d031e Compare June 1, 2026 12:18
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
shortcuts/drive/drive_download.go (1)

88-88: ⚠️ Potential issue | 🟠 Major

Migrate this save-error wrapper to typed errs.* errors

common.WrapSaveErrorByCategory returns legacy *output.ExitError via output.ErrValidation / output.Errorf (not typed *errs.*), so return common.WrapSaveErrorByCategory(err, "io") leaves a non-typed error path in shortcuts/drive/drive_download.go.

Replace with typed errors matching the same branches:

  • fileio.ErrPathValidationerrs.NewValidationError(errs.SubtypeInvalidArgument, "unsafe output path: %s", err)
  • *fileio.MkdirErrorerrs.NewInternalError(errs.SubtypeFileIO, "cannot create parent directory: %s", err)
  • default → errs.NewInternalError(errs.SubtypeFileIO, "cannot create file: %s", err)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@shortcuts/drive/drive_download.go` at line 88, Replace the legacy
common.WrapSaveErrorByCategory(err, "io") return in drive_download.go with typed
errs-based branches: detect fileio.ErrPathValidation and return
errs.NewValidationError(errs.SubtypeInvalidArgument, "unsafe output path: %s",
err); detect a *fileio.MkdirError and return
errs.NewInternalError(errs.SubtypeFileIO, "cannot create parent directory: %s",
err); otherwise return errs.NewInternalError(errs.SubtypeFileIO, "cannot create
file: %s", err); keep the original error variable name and ensure you
import/reference fileio and errs symbols used above.
🧹 Nitpick comments (1)
shortcuts/drive/drive_download.go (1)

79-79: ⚡ Quick win

Remove redundant error text from the message format.

The call wrapDriveNetworkErr(err, "download failed: %s", err) includes the error both in the formatted message and as the cause, resulting in duplicate error text. Since WithCause(err) already attaches the underlying error, the message should be a plain description.

♻️ Proposed fix
-		return wrapDriveNetworkErr(err, "download failed: %s", err)
+		return wrapDriveNetworkErr(err, "download failed")
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@shortcuts/drive/drive_download.go` at line 79, The wrapDriveNetworkErr call
duplicates the error by formatting it into the message and also passing it as
the cause; update the call to use a plain descriptive message instead of
including "%s" and the error. Locate the call to wrapDriveNetworkErr(err,
"download failed: %s", err) in drive_download.go and change it to something like
wrapDriveNetworkErr(err, "download failed") so the underlying err is only
attached via the cause, not interpolated into the message.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@shortcuts/drive/drive_download.go`:
- Line 88: Replace the legacy common.WrapSaveErrorByCategory(err, "io") return
in drive_download.go with typed errs-based branches: detect
fileio.ErrPathValidation and return
errs.NewValidationError(errs.SubtypeInvalidArgument, "unsafe output path: %s",
err); detect a *fileio.MkdirError and return
errs.NewInternalError(errs.SubtypeFileIO, "cannot create parent directory: %s",
err); otherwise return errs.NewInternalError(errs.SubtypeFileIO, "cannot create
file: %s", err); keep the original error variable name and ensure you
import/reference fileio and errs symbols used above.

---

Nitpick comments:
In `@shortcuts/drive/drive_download.go`:
- Line 79: The wrapDriveNetworkErr call duplicates the error by formatting it
into the message and also passing it as the cause; update the call to use a
plain descriptive message instead of including "%s" and the error. Locate the
call to wrapDriveNetworkErr(err, "download failed: %s", err) in
drive_download.go and change it to something like wrapDriveNetworkErr(err,
"download failed") so the underlying err is only attached via the cause, not
interpolated into the message.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 1c5d7533-116d-4f0a-873d-cc176fed60fa

📥 Commits

Reviewing files that changed from the base of the PR and between a3361c0 and 37d031e.

📒 Files selected for processing (40)
  • .golangci.yml
  • errs/types.go
  • errs/types_test.go
  • internal/errclass/codemeta_drive.go
  • internal/errclass/codemeta_drive_test.go
  • shortcuts/drive/drive_add_comment.go
  • shortcuts/drive/drive_add_comment_test.go
  • shortcuts/drive/drive_apply_permission.go
  • shortcuts/drive/drive_create_folder.go
  • shortcuts/drive/drive_create_shortcut.go
  • shortcuts/drive/drive_delete.go
  • shortcuts/drive/drive_download.go
  • shortcuts/drive/drive_duplicate_remote_test.go
  • shortcuts/drive/drive_export.go
  • shortcuts/drive/drive_export_common.go
  • shortcuts/drive/drive_export_download.go
  • shortcuts/drive/drive_export_test.go
  • shortcuts/drive/drive_import.go
  • shortcuts/drive/drive_import_common.go
  • shortcuts/drive/drive_inspect.go
  • shortcuts/drive/drive_move.go
  • shortcuts/drive/drive_move_common.go
  • shortcuts/drive/drive_pull.go
  • shortcuts/drive/drive_pull_test.go
  • shortcuts/drive/drive_push.go
  • shortcuts/drive/drive_search.go
  • shortcuts/drive/drive_search_test.go
  • shortcuts/drive/drive_secure_label.go
  • shortcuts/drive/drive_status.go
  • shortcuts/drive/drive_status_test.go
  • shortcuts/drive/drive_sync.go
  • shortcuts/drive/drive_sync_test.go
  • shortcuts/drive/drive_task_result.go
  • shortcuts/drive/drive_task_result_test.go
  • shortcuts/drive/drive_upload.go
  • shortcuts/drive/drive_upload_classify_test.go
  • shortcuts/drive/drive_version.go
  • shortcuts/drive/drive_version_test.go
  • shortcuts/drive/list_remote.go
  • tests/cli_e2e/drive/drive_duplicate_sync_workflow_test.go
🚧 Files skipped from review as they are similar to previous changes (39)
  • tests/cli_e2e/drive/drive_duplicate_sync_workflow_test.go
  • shortcuts/drive/drive_export_download.go
  • shortcuts/drive/drive_secure_label.go
  • internal/errclass/codemeta_drive_test.go
  • shortcuts/drive/drive_task_result_test.go
  • shortcuts/drive/drive_import.go
  • shortcuts/drive/drive_version_test.go
  • shortcuts/drive/drive_search_test.go
  • shortcuts/drive/drive_create_shortcut.go
  • internal/errclass/codemeta_drive.go
  • shortcuts/drive/drive_delete.go
  • shortcuts/drive/drive_move.go
  • .golangci.yml
  • shortcuts/drive/drive_status_test.go
  • errs/types_test.go
  • errs/types.go
  • shortcuts/drive/drive_export_test.go
  • shortcuts/drive/drive_move_common.go
  • shortcuts/drive/drive_create_folder.go
  • shortcuts/drive/drive_inspect.go
  • shortcuts/drive/drive_duplicate_remote_test.go
  • shortcuts/drive/drive_add_comment_test.go
  • shortcuts/drive/drive_pull.go
  • shortcuts/drive/drive_apply_permission.go
  • shortcuts/drive/drive_version.go
  • shortcuts/drive/list_remote.go
  • shortcuts/drive/drive_task_result.go
  • shortcuts/drive/drive_status.go
  • shortcuts/drive/drive_export_common.go
  • shortcuts/drive/drive_upload_classify_test.go
  • shortcuts/drive/drive_export.go
  • shortcuts/drive/drive_pull_test.go
  • shortcuts/drive/drive_sync_test.go
  • shortcuts/drive/drive_search.go
  • shortcuts/drive/drive_import_common.go
  • shortcuts/drive/drive_push.go
  • shortcuts/drive/drive_upload.go
  • shortcuts/drive/drive_add_comment.go
  • shortcuts/drive/drive_sync.go

@evandance evandance force-pushed the feat/errs-migrate-drive branch 8 times, most recently from 5cf7e04 to 0f40284 Compare June 2, 2026 08:14
@github-actions github-actions Bot added size/XL Architecture-level or global-impact change and removed size/L Large or sensitive change across domains or core paths labels Jun 2, 2026
@evandance evandance force-pushed the feat/errs-migrate-drive branch 3 times, most recently from 6d2f155 to 57072bc Compare June 2, 2026 11:36
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.
@evandance evandance merged commit 98173ae into main Jun 3, 2026
21 checks passed
@evandance evandance deleted the feat/errs-migrate-drive branch June 3, 2026 02:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

domain/ccm PR touches the ccm domain feature size/XL Architecture-level or global-impact change

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants