fix(plain): preserve real ForbiddenError message from Plain API#2167
Conversation
`validateToken` was catching the `ForbiddenError` thrown by `@team-plain/graphql` and re-throwing a new one with a hardcoded "this key cannot read threads" message, discarding the specific permission detail that Plain's API returned in the original error. This led to misleading UI feedback: keys missing unrelated permissions (e.g. `customer:read`) were reported as missing thread read access. Now the SDK's `ForbiddenError` propagates unchanged, so `toPlainErrorMessage` surfaces the exact reason Plain provided. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Greptile SummaryThis PR fixes a misleading error message in
Confidence Score: 5/5Safe to merge — the change is a targeted removal of an error-message rewrite, and error propagation through the existing call chain is logically unchanged for all other error types. The diff is minimal and well-scoped: one branch that was incorrectly wrapping a ForbiddenError is removed, and a misleading fallback hint is cleaned up. The consolidated instanceof guard in validateToken is logically equivalent to the previous four separate checks for all cases. toPlainErrorMessage already preferred error.message first, so the SDK's original reason now flows through to the UI without further change. No files require special attention.
|
| Filename | Overview |
|---|---|
| src/main/core/plain/plain-connection-service.ts | Removes the ForbiddenError re-wrap in validateToken so the SDK's original message propagates, and simplifies the fallback string in toPlainErrorMessage to drop the misleading thread-permissions hint. |
Sequence Diagram
sequenceDiagram
participant UI as Settings UI
participant SVC as PlainConnectionService
participant SDK as @team-plain/graphql
UI->>SVC: saveToken(key)
SVC->>SDK: "client.query.threads({ first: 1 })"
SDK-->>SVC: throws ForbiddenError("customer:read missing")
note over SVC: BEFORE: re-wrap with hardcoded message
note over SVC: AFTER: re-throw original ForbiddenError
SVC->>SVC: toPlainErrorMessage(error, fallback)
note over SVC: returns error.message ("customer:read missing")
SVC-->>UI: "{ success: false, error: "customer:read missing" }"
Reviews (1): Last reviewed commit: "fix(plain): preserve real ForbiddenError..." | Re-trigger Greptile
Summary
PlainConnectionService.validateTokenwas catching theForbiddenErrorthrown by@team-plain/graphqland re-throwing a new one with a hardcoded message:'Insufficient permissions: this key cannot read threads. Ensure thread read permissions are enabled.'That overwrote the SDK's original message, which already contained the specific permission detail Plain's API returned (e.g.
customer:read,thread:list, workspace-scope issues, etc. — seenode_modules/@team-plain/graphql/dist/graphql-client.js:30).The result: users with valid
thread:readpermissions but a different missing scope got a misleading "ensure thread read permissions are enabled" error, sending them on a wild goose chase verifying a permission they already had.This PR drops the rewrite and lets the SDK's
ForbiddenErrorpropagate.toPlainErrorMessagealready returnserror.messagefirst, so the actual Plain-side reason now reaches the settings UI.Test plan
pnpm run formatpnpm run lintpnpm run typecheckpnpm run test(823 passed)🤖 Generated with Claude Code