{"record":{"id":"81b34ff518089564","repo":"CherryHQ/cherry-studio","slug":"socket-mode-connection-failed-data-error-no","errorCode":null,"errorMessage":"Socket Mode connection failed: ${data.error ?? 'no URL returned'}","messagePattern":"Socket Mode connection failed: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/main/ai/channels/adapters/slack/SlackAdapter.ts","lineNumber":275,"sourceCode":"    }\n  }\n\n  private async getSocketModeUrl(): Promise<string> {\n    const response = await net.fetch(`${SLACK_API_BASE}/apps.connections.open`, {\n      method: 'POST',\n      headers: {\n        Authorization: `Bearer ${this.appToken}`,\n        'Content-Type': 'application/x-www-form-urlencoded'\n      }\n    })\n\n    if (!response.ok) {\n      throw new Error(`Failed to open Socket Mode connection: HTTP ${response.status}`)\n    }\n\n    const data = (await response.json()) as { ok: boolean; url?: string; error?: string }\n    if (!data.ok || !data.url) {\n      throw new Error(`Socket Mode connection failed: ${data.error ?? 'no URL returned'}`)\n    }\n\n    return data.url\n  }\n\n  private async startSocketMode(): Promise<void> {\n    if (this.shouldStop) return\n\n    try {\n      this.cleanup()\n\n      const wsUrl = await this.getSocketModeUrl()\n      this.log.info('Connecting to Slack Socket Mode')\n\n      const ws = new WebSocket(wsUrl)\n      this.ws = ws\n\n      ws.on('open', () => {","sourceCodeStart":257,"sourceCodeEnd":293,"githubUrl":"https://github.com/CherryHQ/cherry-studio/blob/726446b54cd69ffe51a276638672f6d95ca0768c/src/main/ai/channels/adapters/slack/SlackAdapter.ts#L257-L293","documentation":"Thrown by SlackAdapter.getSocketModeUrl() when apps.connections.open returns HTTP 2xx but the parsed JSON body has ok:false or lacks the url field. This is Slack's application-level error path — the HTTP transport succeeded but Slack refused to open a Socket Mode session, and Slack includes a short error string (data.error) describing why.","triggerScenarios":"POST to apps.connections.open returns 200 with {ok:false,error:'...'} or 200 with {ok:true} but no url. Known Slack error strings here: 'invalid_auth' (app token bad), 'not_allowed_ip_scope' (IP allowlist on the Enterprise org), 'account_inactive' (app token belongs to a deleted workspace), 'missing_scope' (token lacks connections:write). The ?? 'no URL returned' fallback covers an unexpected schema where ok is true but url is absent — rare, indicates a Slack API change.","commonSituations":"Socket Mode was toggled off in the Slack app settings after the token was created; the app-level token was regenerated on api.slack.com but the channel config still holds the old one; the Slack workspace was deactivated; an Enterprise Grid admin restricted which IPs can open Socket Mode connections.","solutions":["Inspect the data.error string in the thrown message — it maps directly to a Slack documented error code; the fix follows from that specific code.","Re-enable Socket Mode in the Slack app settings (Socket Mode toggles off when scopes are changed and the app is not reinstalled).","If data.error is 'invalid_auth', regenerate the app-level token and update channel config.","If data.error is 'missing_scope', recreate the token granting connections:write."],"exampleFix":"// before — generic message hides which Slack error fired\nif (!data.ok || !data.url) {\n  throw new Error(`Socket Mode connection failed: ${data.error ?? 'no URL returned'}`)\n}\n\n// after — surface the actionable Slack error code for common cases\nif (!data.ok || !data.url) {\n  const hint = data.error === 'invalid_auth'\n    ? ' (regenerate the xapp- token at api.slack.com/apps)'\n    : data.error === 'missing_scope' ? ' (token needs connections:write)' : ''\n  throw new Error(`Socket Mode connection failed: ${data.error ?? 'no URL returned'}${hint}`)\n}","handlingStrategy":"try-catch","validationCode":"// You cannot fully prevent Slack's application-level ok:false without calling the API,\n// but you can pre-validate token shape to eliminate the most common cause (invalid_auth).\nconst APP_TOKEN_RE = /^xapp-1-[A-Za-z0-9-]+$/ // connections:write scope must also be granted\n\nif (!APP_TOKEN_RE.test(config.app_token)) {\n  throw new ConfigError('Slack app token must be xapp-1-... with connections:write scope')\n}","typeGuard":"// Type guard for the apps.connections.open success body\ninterface SocketModeOpenOk {\n  ok: true\n  url: string\n}\nfunction isSocketModeOpenOk(data: unknown): data is SocketModeOpenOk {\n  return (\n    typeof data === 'object' && data !== null &&\n    (data as { ok?: boolean }).ok === true &&\n    typeof (data as { url?: unknown }).url === 'string'\n  )\n}","tryCatchPattern":"try {\n  await adapter.connect(signal)\n} catch (e) {\n  const msg = e instanceof Error ? e.message : ''\n  if (msg.startsWith('Socket Mode connection failed:')) {\n    const slackError = msg.slice('Socket Mode connection failed:'.length).trim()\n    if (slackError === 'invalid_auth') {\n      surfaceUserError('Slack app token is invalid — regenerate at api.slack.com/apps')\n    } else if (slackError === 'missing_scope') {\n      surfaceUserError('Recreate the app token granting connections:write')\n    } else {\n      surfaceUserError(`Slack refused Socket Mode: ${slackError}`)\n    }\n  }\n}","preventionTips":["Keep Socket Mode enabled in the Slack app settings — toggling scopes off re-disables it.","After changing app scopes, always reinstall the app to the workspace so tokens refresh.","Store the xapp- token in a config field validated against the xapp-1- prefix on write.","Log the Slack error string verbatim when this throws — it maps to documented Slack error codes."],"tags":["slack","socket-mode","authentication","config","api-error"],"backgroundTag":null,"analyzedSha":"726446b54cd69ffe51a276638672f6d95ca0768c","analyzedAt":"2026-08-12T17:30:37.448Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}