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

  1. Verify the app-level token (xapp-) is set and not confused with the bot token (xoxb-) — SlackAdapter.checkReady() requires both (SlackAdapter.ts:222).
  2. In the Slack app config (api.slack.com/apps), confirm Socket Mode is enabled and the app-level token has the connections:write scope.
  3. If the token was regenerated, update the channel config with the new xapp- token and reconnect.
  4. 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

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


AI-assisted analysis of CherryHQ/cherry-studio@726446b54c (2026-08-12). Data as JSON: /api/errors/f255d16bd6f58bf8. Report an issue: GitHub.