CherryHQ/cherry-studio · error · Error
Failed to open Socket Mode connection: HTTP ${response.statu
Error message
Failed to open Socket Mode connection: HTTP ${response.status} What it means
Thrown by SlackAdapter.getSocketModeUrl() after POSTing to Slack's apps.connections.open endpoint when the HTTP response status is not 2xx (response.ok is false). This is a transport-level failure: Slack rejected the request before returning a Socket Mode WSS URL. The app-level token (xapp-) is the credential used here, distinct from the bot token (xoxb-).
Source
Thrown at src/main/ai/channels/adapters/slack/SlackAdapter.ts:270
this.log.info('Slack bot identity resolved', { botUserId: this.botUserId })
} catch (error) {
this.log.warn('Failed to resolve bot user ID', {
error: error instanceof Error ? error.message : String(error)
})
}
}
private async getSocketModeUrl(): Promise<string> {
const response = await net.fetch(`${SLACK_API_BASE}/apps.connections.open`, {
method: 'POST',
headers: {
Authorization: `Bearer ${this.appToken}`,
'Content-Type': 'application/x-www-form-urlencoded'
}
})
if (!response.ok) {
throw new Error(`Failed to open Socket Mode connection: HTTP ${response.status}`)
}
const data = (await response.json()) as { ok: boolean; url?: string; error?: string }
if (!data.ok || !data.url) {
throw new Error(`Socket Mode connection failed: ${data.error ?? 'no URL returned'}`)
}
return data.url
}
private async startSocketMode(): Promise<void> {
if (this.shouldStop) return
try {
this.cleanup()
const wsUrl = await this.getSocketModeUrl()
this.log.info('Connecting to Slack Socket Mode')View on GitHub (pinned to 726446b54c)
Solutions
- Verify the app-level token (xapp-) is set and not confused with the bot token (xoxb-) — SlackAdapter.checkReady() requires both (SlackAdapter.ts:222).
- In the Slack app config (api.slack.com/apps), confirm Socket Mode is enabled and the app-level token has the connections:write scope.
- If the token was regenerated, update the channel config with the new xapp- token and reconnect.
- Check status.slack.com for an active incident if the token is valid and the HTTP status is 5xx.
Example fix
// before — wrong token type silently produces 401
this.appToken = config.channelConfig.app_token // accidentally holds an xoxb- token
// after — guard at connect time so the error is self-describing
protected override async performConnect(_signal: AbortSignal): Promise<void> {
if (!this.appToken?.startsWith('xapp-')) {
throw new Error('Slack app-level token (xapp-) is required for Socket Mode')
}
// ...
} Defensive patterns
Strategy: validation
Validate before calling
// Validate the app-level token shape and readiness BEFORE calling performConnect.
// SlackAdapter.checkReady() (SlackAdapter.ts:222) already returns false for missing tokens;
// honor it and add a format check.
const APP_TOKEN_RE = /^xapp-1-[A-Za-z0-9-]+$/
function canOpenSocketMode(appToken: string | undefined): boolean {
return typeof appToken === 'string' && APP_TOKEN_RE.test(appToken)
}
// Usage:
if (!canOpenSocketMode(channel.config.app_token)) {
throw new ConfigError('Slack app-level token (xapp-) is required and must be enabled for Socket Mode')
}
await slackAdapter.connect(signal) Type guard
function isValidSlackAppToken(v: unknown): v is string {
return typeof v === 'string' && /^xapp-1-[A-Za-z0-9-]+/.test(v)
}
// Before connecting:
if (!isValidSlackAppToken(config.app_token) || !isValidSlackBotToken(config.bot_token)) {
return { ok: false, reason: 'invalid-tokens' }
} Try / catch
// getSocketModeUrl is called inside startSocketMode which already wraps in try/catch
// and calls scheduleReconnect(). Do NOT catch here — let it propagate so the reconnect
// loop runs. Instead, catch at the adapter-connection boundary and surface a state:
try {
await adapter.connect(signal)
} catch (e) {
if (e instanceof Error && e.message.startsWith('Failed to open Socket Mode connection')) {
setChannelState(channelId, 'error', 'Slack Socket Mode handshake failed — check the xapp- token and Socket Mode scope')
}
throw e
} Prevention
- Enable Socket Mode and grant connections:write on the app-level token before configuring the channel.
- Distinguish xapp- (app token) from xoxb- (bot token) at config validation time using a prefix regex.
- Honor checkReady() before calling connect() so invalid configs never reach the network call.
- Regenerate tokens on api.slack.com whenever scopes change and reinstall the app to the workspace.
When it happens
Trigger: A POST to https://slack.com/api/apps.connections.open with the app-level Bearer token returns a non-2xx status. Common causes: the xapp- token is missing/expired/revoked (Slack returns 401), the token has wrong scopes (Socket Mode must be enabled on the app and the token needs connections:write), or Slack itself is returning 5xx during an incident. Unlike the JSON ok:false path, this fires only when the HTTP layer itself is unhealthy.
Common situations: The Slack app config was created without enabling Socket Mode, so the app-level token lacks the connections:write scope; the user pasted a bot token (xoxb-) into the app-token field; the token was revoked when the Slack app was reinstalled or the workspace disconnected the app; a corporate proxy/firewall returns a 407 or 502 for slack.com.
Related errors
- Socket Mode connection failed: ${data.error ?? 'no URL retur
- Slack API error ${method}: ${data.error ?? 'unknown error'}
- Slack bot token (xoxb-...) is required
- Slack API error ${method}: HTTP ${response.status} - ${error
- OAuth authentication failed: ${oauthError instanceof Error ?
AI-assisted analysis of CherryHQ/cherry-studio@726446b54c (2026-08-12).
Data as JSON: /api/errors/f255d16bd6f58bf8.
Report an issue: GitHub.