diff --git a/internal/output/lark_errors.go b/internal/output/lark_errors.go index a2ac2590e..418acf15a 100644 --- a/internal/output/lark_errors.go +++ b/internal/output/lark_errors.go @@ -39,6 +39,10 @@ const ( LarkErrDriveCrossTenantUnit = 1064510 // cross tenant and unit not support LarkErrDriveCrossBrand = 1064511 // cross brand not support + // Wiki write-path lock contention (e.g. concurrent wiki +node-create under the + // same parent). Server-side write lock; transient, safe to retry with backoff. + LarkErrWikiLockContention = 131009 + // Sheets float image: width/height/offset out of range or invalid. LarkErrSheetsFloatImageInvalidDims = 1310246 @@ -83,6 +87,8 @@ func ClassifyLarkError(code int, msg string) (int, string, string) { // drive-specific constraints that benefit from actionable hints case LarkErrDriveResourceContention: return ExitAPI, "conflict", "please retry later and avoid concurrent duplicate requests" + case LarkErrWikiLockContention: + return ExitAPI, "conflict", "wiki write lock contention on this parent node; retry with exponential backoff or serialize sibling-node writes" case LarkErrDriveCrossTenantUnit: return ExitAPI, "cross_tenant_unit", "operate on source and target within the same tenant and region/unit" case LarkErrDriveCrossBrand: diff --git a/internal/output/lark_errors_test.go b/internal/output/lark_errors_test.go index a9af905c5..ae540d39b 100644 --- a/internal/output/lark_errors_test.go +++ b/internal/output/lark_errors_test.go @@ -90,3 +90,24 @@ func TestClassifyLarkError_DriveCreateShortcutConstraints(t *testing.T) { }) } } + +// TestClassifyLarkError_WikiLockContention verifies the wiki write-lock +// contention error (131009) maps to an actionable retry hint instead of +// a generic "api_error". Surfaces during concurrent wiki +node-create +// against the same parent (see larksuite/cli#1012). +func TestClassifyLarkError_WikiLockContention(t *testing.T) { + t.Parallel() + gotExitCode, gotType, gotHint := ClassifyLarkError(LarkErrWikiLockContention, "raw msg") + if gotExitCode != ExitAPI { + t.Fatalf("exitCode=%d, want %d", gotExitCode, ExitAPI) + } + if gotType != "conflict" { + t.Fatalf("type=%q, want %q", gotType, "conflict") + } + if !strings.Contains(gotHint, "wiki write lock") { + t.Fatalf("hint=%q, want substring %q", gotHint, "wiki write lock") + } + if !strings.Contains(gotHint, "backoff") { + t.Fatalf("hint=%q, want substring %q", gotHint, "backoff") + } +}