Skip to content

Improve drive batch failure handling - #1703

Merged
fangshuyu-768 merged 6 commits into
mainfrom
codex/drive-push-p0-p1-reliability
Jul 1, 2026
Merged

Improve drive batch failure handling#1703
fangshuyu-768 merged 6 commits into
mainfrom
codex/drive-push-p0-p1-reliability

Conversation

@fangshuyu-768

@fangshuyu-768 fangshuyu-768 commented Jul 1, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • classify common Drive API codes so agent-visible errors carry actionable subtype/retryable metadata across drive +push, drive +pull, and drive +sync
  • add per-item phase / error_class / code / subtype / retryable fields for push, pull, and sync partial failures
  • abort remaining batch work after terminal global failures such as missing scopes, permission denial, invalid API parameters, rate limits classified by existing API paths, and server-side global failures
  • preflight drive +sync action scopes (drive:file:download, drive:file:upload, space:folder:create) before listing/walking so missing grants surface as the standard missing_scope permission error before any partial sync can start
  • validate push upload requests and re-check local file snapshots before upload to avoid size mismatch calls when files change during push

Tests

  • go test ./internal/client ./internal/errclass ./shortcuts/drive
  • go build ./...
  • Earlier full go test ./... was also attempted and failed in unrelated environment/live/e2e areas: schema/manifest catalog fixtures missing services, temp git cleanup in publiccontent, missing local ./lark-cli for e2e dry-runs, and live auth/timeout failures in calendar/im/task tests.

Summary by CodeRabbit

  • New Features
    • Drive push, pull, and sync now emit structured per-item failure details (phase, error class, code, subtype, retryable).
    • Commands now stop earlier on terminal failures and set an overall aborted status.
  • Bug Fixes
    • Improved failure classification across push/pull/sync, including Drive permission, not-found, quota, and internal error handling.
    • Added stronger push upload validation and snapshot consistency checks to prevent stale/changed uploads.
  • Tests
    • Added/updated regression tests to verify terminal-failure abort behavior, structured partial-failure payloads, and deterministic stdout parsing for assertions.

@github-actions github-actions Bot added domain/ccm PR touches the ccm domain size/M Single-domain feat or fix with limited business impact labels Jul 1, 2026
@coderabbitai

coderabbitai Bot commented Jul 1, 2026

Copy link
Copy Markdown

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 structured Drive failure metadata to push, pull, and sync item payloads, expands Drive code metadata mappings, adds terminal-failure abort handling, and updates tests to assert structured partial-failure output.

Changes

Drive batch failure classification

Layer / File(s) Summary
Drive code metadata
internal/errclass/codemeta_drive.go, internal/errclass/codemeta_test.go
driveCodeMeta gains new Drive error-code mappings with category, subtype, and retryable fields, and a lookup test validates the added codes.
Push failure classification and aborts
shortcuts/drive/drive_push.go
drivePushItem gains structured failure fields; helpers classify batch errors, stop upload/delete processing on terminal failures, and set summary.aborted; upload request and snapshot validation run before uploading.
Push regression tests
shortcuts/drive/drive_push_test.go
Adds abort regressions for delete, upload-parameter, missing-scope, and local-file-change cases; updates stdout decoding helpers and structured item assertions.
Pull failure classification and aborts
shortcuts/drive/drive_pull.go, shortcuts/drive/drive_pull_test.go
drivePullItem gains structured failure fields; download and delete-local failures are classified, terminal failures stop the loop, summary.aborted is added, and a new HTTP 403 regression test covers the abort path.
Sync failure classification and aborts
shortcuts/drive/drive_sync.go, shortcuts/drive/drive_sync_test.go
driveSyncItem gains structured failure fields; create-folder, download, upload, and conflict-resolution failures are classified, terminal failures stop later work, summary.aborted is added, and the sync test helper plus a new HTTP 403 regression test are updated.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant drivePushUploadFile
  participant drivePushFailedItem
  participant driveClassifyBatchFailure
  participant summary

  drivePushUploadFile->>drivePushFailedItem: record upload/delete failure
  drivePushFailedItem->>driveClassifyBatchFailure: classify(err)
  driveClassifyBatchFailure-->>drivePushFailedItem: Class, Code, Subtype, Retryable, Terminal
  drivePushFailedItem-->>drivePushUploadFile: structured drivePushItem
  drivePushUploadFile->>drivePushUploadFile: break on terminal failure
  drivePushUploadFile->>summary: aborted = hasTerminalFailure(items)
Loading

Possibly related PRs

  • larksuite/cli#696: Modifies the same drive +pull command that this PR extends with structured failure metadata and abort behavior.
  • larksuite/cli#709: Introduces the drive +push flow that this PR updates with typed failure reporting and terminal-abort handling.
  • larksuite/cli#1205: Extends Drive error-code mappings in internal/errclass, matching the metadata changes used here.

Suggested labels: feature

Suggested reviewers: wittam-01

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 36.36% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and accurately summarizes the main change: improved drive batch failure handling.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description check ✅ Passed The description covers the summary and test verification, but it omits the template's explicit Changes and Related Issues sections.
✨ 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 codex/drive-push-p0-p1-reliability

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@codecov

codecov Bot commented Jul 1, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 70.66116% with 71 lines in your changes missing coverage. Please review.
✅ Project coverage is 74.48%. Comparing base (3bda9e1) to head (67f75c4).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
shortcuts/drive/drive_push.go 69.17% 30 Missing and 11 partials ⚠️
shortcuts/drive/drive_sync.go 64.86% 18 Missing and 8 partials ⚠️
shortcuts/drive/drive_pull.go 88.57% 3 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1703      +/-   ##
==========================================
- Coverage   74.48%   74.48%   -0.01%     
==========================================
  Files         850      850              
  Lines       86703    86878     +175     
==========================================
+ Hits        64583    64709     +126     
- Misses      17175    17210      +35     
- Partials     4945     4959      +14     

☔ View full report in Codecov by Harness.
📢 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.

@github-actions

github-actions Bot commented Jul 1, 2026

Copy link
Copy Markdown

🚀 PR Preview Install Guide

🧰 CLI update

npm i -g https://pkg.pr.new/larksuite/cli/@larksuite/cli@67f75c4f2d74ea31dc212b8166ce45aa1c38e449

🧩 Skill update

npx skills add larksuite/cli#codex/drive-push-p0-p1-reliability -y -g

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🤖 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_push_test.go`:
- Around line 987-1048: The new drive push error tests in
TestDrivePushAbortsAfterUploadParamsError and the related upload failure cases
are asserting metadata by scanning raw stdout strings, which is brittle and
doesn’t prove the values are attached to the intended JSON fields. Update these
tests to parse stdout as JSON and assert the structured payload fields directly
(for example the item error metadata like error_class, code, subtype, aborted,
and phase), and use errs.ProblemOf for typed error metadata/cause checks instead
of message substring matching alone.

In `@shortcuts/drive/drive_push.go`:
- Line 12: The snapshot verification in drive_push.go is using direct os.Stat
access instead of the shortcut filesystem abstraction. Update the verifier and
related call sites to accept runtime and use runtime.FileIO().Stat on
file.OpenPath rather than file.AbsPath, so user-facing paths continue to flow
through the VFS/mocking layer. Keep the fix localized around the snapshot
verification helpers and any places that currently import os only for Stat.
- Line 43: The structured failure payload in the drive push flow is dropping
Retryable when it is false because the DriveItemFailure JSON tag uses omitempty.
Update the failure serialization in drive_push.go, including the related failure
items around the referenced block, so Retryable is always emitted and preserves
retryable:false for non-retryable failures. Keep the change focused on the
DriveItemFailure type and any constructors/emitters that build the failed item
JSON.
- Around line 407-408: The delete failure handling in drivePush currently
ignores the terminal flag returned by drivePushFailedItem, so non-retryable
auth/permission errors keep retrying delete calls while the summary still shows
aborted. Update the delete-error path in drivePush to capture and respect the
terminal value from drivePushFailedItem, and stop further delete attempts when
it indicates a terminal failure. Apply the same fix in both delete failure
branches referenced by drivePushFailedItem so terminal delete failures are
honored consistently.
🪄 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: 9cb6aa6e-769f-4757-9477-936d20b942e0

📥 Commits

Reviewing files that changed from the base of the PR and between 3bda9e1 and e7fcdbd.

📒 Files selected for processing (4)
  • internal/errclass/codemeta_drive.go
  • internal/errclass/codemeta_test.go
  • shortcuts/drive/drive_push.go
  • shortcuts/drive/drive_push_test.go

Comment thread shortcuts/drive/drive_push_test.go
Comment thread shortcuts/drive/drive_push.go Outdated
Comment thread shortcuts/drive/drive_push.go Outdated
Comment thread shortcuts/drive/drive_push.go Outdated
@github-actions github-actions Bot added size/L Large or sensitive change across domains or core paths and removed size/M Single-domain feat or fix with limited business impact labels Jul 1, 2026
@fangshuyu-768 fangshuyu-768 changed the title Improve drive +push failure handling Improve drive batch failure handling Jul 1, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (2)
internal/client/client.go (1)

247-277: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Add test coverage for the HTTP 403 + unclassifiable-JSON fallback branch.

Lines 268-274 (the status == http.StatusForbidden fallback) only fire when the body parses as valid, non-empty JSON but BuildAPIError can't classify it (missing/zero code). Neither existing test exercises this: TestDoStream_HTTPErrorIncludesLogID uses a non-JSON body (so classification bails out before reaching this branch), and the new TestDoStream_JSONHTTPErrorIsClassified uses a classifiable code. This new branch is currently untested.

As per coding guidelines, "Every behavior change needs a test alongside the change."

✅ Suggested additional test case
func TestDoStream_403UnclassifiedJSONFallsBackToPermissionDenied(t *testing.T) {
	t.Setenv("LARKSUITE_CLI_CONFIG_DIR", t.TempDir())

	config := &core.CliConfig{AppID: "test-app", AppSecret: "test-secret", Brand: core.BrandFeishu}
	factory, _, _, reg := cmdutil.TestFactory(t, config)
	reg.Register(&httpmock.Stub{
		Method:  http.MethodGet,
		URL:     "/open-apis/drive/v1/files/file_token/download",
		Status:  http.StatusForbidden,
		RawBody: []byte(`{"error":"forbidden"}`),
	})

	client, err := factory.NewAPIClientWithConfig(config)
	if err != nil {
		t.Fatalf("NewAPIClientWithConfig() error = %v", err)
	}

	_, err = client.DoStream(context.Background(), &larkcore.ApiReq{
		HttpMethod: http.MethodGet,
		ApiPath:    "/open-apis/drive/v1/files/file_token/download",
	}, core.AsBot)
	var permErr *errs.PermissionError
	if !errors.As(err, &permErr) {
		t.Fatalf("expected *errs.PermissionError, got %T %v", err, err)
	}
	if permErr.Subtype != errs.SubtypePermissionDenied {
		t.Fatalf("Subtype = %q, want %q", permErr.Subtype, errs.SubtypePermissionDenied)
	}
}
🤖 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 `@internal/client/client.go` around lines 247 - 277, Add a test for the
unclassified JSON HTTP 403 fallback in classifyStreamAPIError. The current
coverage misses the branch where rawBody is valid non-empty JSON, BuildAPIError
returns nil, and status == http.StatusForbidden, so add a DoStream test in the
client API suite that returns a 403 with a JSON body lacking a classifiable code
and assert it falls back to errs.PermissionError with SubtypePermissionDenied.
Use the existing helpers and symbols DoStream, classifyStreamAPIError, and
errs.PermissionError so the new case is easy to locate and maintain.

Source: Coding guidelines

internal/client/dostream_test.go (1)

53-93: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Assert Category alongside Subtype/Code/Retryable/LogID.

The test checks apiErr.Code, .Subtype, .Retryable, .LogID but not .Category.

As per path instructions for **/*_test.go, "Error-path tests must assert typed metadata via errs.ProblemOf (category / subtype / param) and cause preservation, not message substrings alone."

✅ Suggested addition
 	if apiErr.Subtype != errs.SubtypeRateLimit {
 		t.Fatalf("Subtype = %q, want %q", apiErr.Subtype, errs.SubtypeRateLimit)
 	}
+	if apiErr.Category != errs.CategoryAPI {
+		t.Fatalf("Category = %q, want %q", apiErr.Category, errs.CategoryAPI)
+	}
 	if !apiErr.Retryable {
🤖 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 `@internal/client/dostream_test.go` around lines 53 - 93, The test for DoStream
stream error classification is missing an assertion for the error category
metadata. Update TestDoStream_JSONHTTPErrorIsClassified to verify
apiErr.Category in addition to Code, Subtype, Retryable, and LogID, and keep
using the typed APIError returned from client.DoStream so the error-path test
validates the structured metadata expected by errs.ProblemOf.

Source: Path instructions

🤖 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_pull.go`:
- Line 49: The Drive pull result serialization is omitting retryable=false
because the bool field uses omitempty; update the result type in drive_pull.go
to use a pointer for Retryable and set it only on failed item payloads so
successful items still omit the field. Adjust the affected constructors/handlers
around the Drive pull flow so non-retryable failures serialize retryable:false
while retryable/unspecified cases remain absent.
- Around line 284-285: The delete fallback in drivePull currently passes the raw
os.Remove error into drivePullFailedItem, which drops the file_io subtype and
cause metadata. Wrap the remove failure as a typed internal file I/O error
before classification, using errs.NewInternalError with the file I/O subtype and
chaining the original error with .WithCause(err), then pass that wrapped error
into drivePullFailedItem in drivePull.

In `@shortcuts/drive/drive_sync.go`:
- Line 38: The drive sync failure payload currently omits retryable when it is
false because driveSyncItem.Retryable uses omitempty on a plain bool. Update
driveSyncItem and driveSyncFailedItem in drive_sync.go to represent Retryable as
a pointer and always set it for failed items so false is serialized explicitly,
then adjust any tests/helpers that read or decode driveSyncItem.Retryable to
handle the pointer-based field consistently.
- Around line 513-516: The rollback path in driveSync’s download handling is
overwriting the original drivePullDownload error with a new file_io error, which
can downgrade the error classification and corrupt retryable/terminal metadata.
In the rollback-failure branch, preserve the original download error value and
only record or attach the rollback failure separately, so driveSyncFailedItem
still receives the typed error returned by the lower layer. Use the existing
driveSyncFailedItem and rollbackErr handling in drive_sync.go to keep the
original err unchanged when rollback also fails.

---

Nitpick comments:
In `@internal/client/client.go`:
- Around line 247-277: Add a test for the unclassified JSON HTTP 403 fallback in
classifyStreamAPIError. The current coverage misses the branch where rawBody is
valid non-empty JSON, BuildAPIError returns nil, and status ==
http.StatusForbidden, so add a DoStream test in the client API suite that
returns a 403 with a JSON body lacking a classifiable code and assert it falls
back to errs.PermissionError with SubtypePermissionDenied. Use the existing
helpers and symbols DoStream, classifyStreamAPIError, and errs.PermissionError
so the new case is easy to locate and maintain.

In `@internal/client/dostream_test.go`:
- Around line 53-93: The test for DoStream stream error classification is
missing an assertion for the error category metadata. Update
TestDoStream_JSONHTTPErrorIsClassified to verify apiErr.Category in addition to
Code, Subtype, Retryable, and LogID, and keep using the typed APIError returned
from client.DoStream so the error-path test validates the structured metadata
expected by errs.ProblemOf.
🪄 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: db5297f2-c8e8-40c3-a88f-9bb3fd55eb25

📥 Commits

Reviewing files that changed from the base of the PR and between e7fcdbd and f081206.

📒 Files selected for processing (7)
  • internal/client/client.go
  • internal/client/dostream_test.go
  • shortcuts/drive/drive_pull.go
  • shortcuts/drive/drive_pull_test.go
  • shortcuts/drive/drive_push.go
  • shortcuts/drive/drive_sync.go
  • shortcuts/drive/drive_sync_test.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • shortcuts/drive/drive_push.go

Comment thread shortcuts/drive/drive_pull.go Outdated
Comment thread shortcuts/drive/drive_pull.go Outdated
Comment thread shortcuts/drive/drive_sync.go Outdated
Comment thread shortcuts/drive/drive_sync.go Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 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_push_test.go`:
- Around line 1273-1278: The snapshot-error test in the drive push path only
checks the subtype, so it does not fully validate the typed error contract.
Update the test around ProblemOf/problemErr to also assert the expected
category, and verify cause preservation by checking that
errors.Unwrap(problemErr) is non-nil; keep using errs.ProblemOf to inspect the
typed metadata instead of relying on message text.

In `@shortcuts/drive/drive_push.go`:
- Around line 787-788: The snapshot verification in drivePushLocalFile only
compares size, so a same-size edit can slip through; update the push guard in
drive_push.go to also compare the captured ModTime from drivePushLocalFile
against the current file info before uploading. If either size or ModTime
differs, return the same failed-precondition validation error from the existing
local file change check. Add a regression test around the push/snapshot
verification path to cover a same-size mutation being rejected.
🪄 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: bb15a6c6-ed7f-4f6b-96c4-5e501b26a0e4

📥 Commits

Reviewing files that changed from the base of the PR and between f081206 and e1052c5.

📒 Files selected for processing (5)
  • shortcuts/drive/drive_pull.go
  • shortcuts/drive/drive_push.go
  • shortcuts/drive/drive_push_test.go
  • shortcuts/drive/drive_sync.go
  • shortcuts/drive/drive_sync_test.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • shortcuts/drive/drive_pull.go
  • shortcuts/drive/drive_sync.go

Comment thread shortcuts/drive/drive_push_test.go
Comment thread shortcuts/drive/drive_push.go
@fangshuyu-768
fangshuyu-768 merged commit a6797ac into main Jul 1, 2026
37 checks passed
@fangshuyu-768
fangshuyu-768 deleted the codex/drive-push-p0-p1-reliability branch July 1, 2026 10:15
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 size/L Large or sensitive change across domains or core paths

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants