From ae3d352f6b907750d0ade31afb5c67ccd4d1418b Mon Sep 17 00:00:00 2001 From: leeguooooo Date: Thu, 21 May 2026 13:23:12 +0900 Subject: [PATCH] fix(output): classify wiki lock-contention error (131009) with retry hint Wiki write-path operations (most commonly `wiki +node-create` against the same parent) surface code 131009 "lock contention" under concurrent calls. Currently this falls through to the generic "api_error" classification, giving users no hint that it is transient and safe to retry. Mirror the existing `LarkErrDriveResourceContention` (1061045) treatment: add a named constant, classify as "conflict", and emit a hint that points the caller toward exponential backoff or serializing sibling-node writes. Refs: #1012 --- internal/output/lark_errors.go | 6 ++++++ internal/output/lark_errors_test.go | 21 +++++++++++++++++++++ 2 files changed, 27 insertions(+) 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") + } +}