Skip to content

StreamingTranscriber Authentication Fails in Browser Environment #90

@xdmarttt

Description

@xdmarttt

The StreamingTranscriber class fails to authenticate properly in browser environments, throwing the:

SyntaxError: Failed to construct 'WebSocket': The subprotocol '[object Object]' is invalid.
at factory (/node_modules/.vite/deps/assemblyai.js?v=dc373e06:132:12)

This occurs because the StreamingTranscriber incorrectly passes authentication headers to the WebSocket constructor, which is not supported in browser environments.

This bug completely breaks the StreamingTranscriber functionality in browser environments.

Environment

  • SDK Version: Latest TypeScript SDK
  • Environment: Browser (client-side)
  • Authentication Method: Both API key and temporary token fail
  • Error Location: polyfillWebSocketFactory call in connect() method

Root Cause Analysis

The issue stems from inconsistent authentication handling between StreamingTranscriber and RealtimeTranscriber classes:

❌ Current Broken Implementation (StreamingTranscriber)

https://github.com/AssemblyAI/assemblyai-node-sdk/blob/main/src/services/streaming/service.ts#L121-L123

✅ Working Implementation (RealtimeTranscriber)

https://github.com/AssemblyAI/assemblyai-node-sdk/blob/main/src/services/realtime/service.ts#L185-L205

Key Issues in StreamingTranscriber

  1. Browser WebSocket API Incompatibility: The StreamingTranscriber always passes a headers object to polyfillWebSocketFactory(), but browser WebSocket constructors don't support custom headers. The browser WebSocket API signature is:

    new WebSocket(url, protocols?) // protocols is string[] or string, NOT an object

    When {headers: {...}} is passed as the second parameter, browsers interpret it as subprotocols, causing the [object Object] error.

  2. Headers passed for token authentication: When using a temporary token, the StreamingTranscriber still tries to pass headers to the WebSocket constructor, which fails in browsers since tokens should be sent via URL parameters instead.

  3. Missing token URL parameter: Unlike RealtimeTranscriber, the StreamingTranscriber doesn't add the token to the WebSocket URL as a query parameter in the connectionUrl() method.

  4. No browser environment detection: StreamingTranscriber lacks the browser compatibility warning that RealtimeTranscriber provides.

Proposed Fix

1. Update connect() method in StreamingTranscriber:

connect() {
  return new Promise<BeginEvent>((resolve) => {
    if (this.socket) {
      throw new Error("Already connected");
    }

    const url = this.connectionUrl();

    // Handle authentication like RealtimeTranscriber
    if (this.token) {
      this.socket = polyfillWebSocketFactory(url.toString());
    } else {
      if (conditions.browser) {
        console.warn(
          `API key authentication is not supported for StreamingTranscriber in browser environment. Use temporary token authentication instead.
Learn more at https://github.com/AssemblyAI/assemblyai-node-sdk/blob/main/docs/compat.md#browser-compatibility.`
        );
      }
      this.socket = polyfillWebSocketFactory(url.toString(), {
        headers: { Authorization: this.apiKey },
      });
    }

    // ... rest of the method
  });
}

2. Update connectionUrl() method in StreamingTranscriber:

private connectionUrl(): URL {
  const url = new URL(this.params.websocketBaseUrl ?? "");

  if (url.protocol !== "wss:") {
    throw new Error("Invalid protocol, must be wss");
  }

  const searchParams = new URLSearchParams();

  // Add token to URL parameters like RealtimeTranscriber
  if (this.token) {
    searchParams.set("token", this.token);
  }

  searchParams.set("sample_rate", this.params.sampleRate.toString());

  // ... rest of existing parameter handling

  url.search = searchParams.toString();
  return url;
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions