diff --git a/CHANGELOG.md b/CHANGELOG.md index a2dedb31232..9c1a298b51b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -158,6 +158,7 @@ Docs: https://docs.openclaw.ai - Feishu/Probe failure backoff: cache API and timeout probe failures for one minute per account key while preserving abort-aware probe timeouts, reducing repeated health-check retries during transient credential/network outages. (#29970) - Feishu/Streaming block fallback: preserve markdown block stream text as final streaming-card content when final payload text is missing, while still suppressing non-card internal block chunk delivery. (#30663) - Feishu/Bitable API errors: unify Feishu Bitable tool error handling with structured `LarkApiError` responses and consistent API/context attribution across wiki/base metadata, field, and record operations. (#31450) +- Feishu/Missing-scope grant URL fix: rewrite known invalid scope aliases (`contact:contact.base:readonly`) to valid scope names in permission grant links, so remediation URLs open with correct Feishu consent scopes. (#31943) - BlueBubbles/Message metadata: harden send response ID extraction, include sender identity in DM context, and normalize inbound `message_id` selection to avoid duplicate ID metadata. (#23970) Thanks @tyler6204. - WebChat/markdown tables: ensure GitHub-flavored markdown table parsing is explicitly enabled at render time and add horizontal overflow handling for wide tables, with regression coverage for table-only and mixed text+table content. (#32365) Thanks @BlueBirdBack. - Feishu/default account resolution: always honor explicit `channels.feishu.defaultAccount` during outbound account selection (including top-level-credential setups where the preferred id is not present in `accounts`), instead of silently falling back to another account id. (#32253) Thanks @bmendonca3. diff --git a/extensions/feishu/src/bot.ts b/extensions/feishu/src/bot.ts index e6ac426649a..1b4f2211b16 100644 --- a/extensions/feishu/src/bot.ts +++ b/extensions/feishu/src/bot.ts @@ -47,6 +47,22 @@ type PermissionError = { const IGNORED_PERMISSION_SCOPE_TOKENS = ["contact:contact.base:readonly"]; +// Feishu API sometimes returns incorrect scope names in permission error +// responses (e.g. "contact:contact.base:readonly" instead of the valid +// "contact:user.base:readonly"). This map corrects known mismatches. +const FEISHU_SCOPE_CORRECTIONS: Record = { + "contact:contact.base:readonly": "contact:user.base:readonly", +}; + +function correctFeishuScopeInUrl(url: string): string { + let corrected = url; + for (const [wrong, right] of Object.entries(FEISHU_SCOPE_CORRECTIONS)) { + corrected = corrected.replaceAll(encodeURIComponent(wrong), encodeURIComponent(right)); + corrected = corrected.replaceAll(wrong, right); + } + return corrected; +} + function shouldSuppressPermissionErrorNotice(permissionError: PermissionError): boolean { const message = permissionError.message.toLowerCase(); return IGNORED_PERMISSION_SCOPE_TOKENS.some((token) => message.includes(token)); @@ -72,7 +88,7 @@ function extractPermissionError(err: unknown): PermissionError | null { // Extract the grant URL from the error message (contains the direct link) const msg = feishuErr.msg ?? ""; const urlMatch = msg.match(/https:\/\/[^\s,]+\/app\/[^\s,]+/); - const grantUrl = urlMatch?.[0]; + const grantUrl = urlMatch?.[0] ? correctFeishuScopeInUrl(urlMatch[0]) : undefined; return { code: feishuErr.code,