Skip to content

AWS Transcribe Streaming WebSocket Disconnects Immediately #3015

@brightversion1

Description

@brightversion1

Environment information

Service: AWS Transcribe (Streaming WebSocket API)

Frontend: React + WebSocket + MediaRecorder/AudioContext

Backend: AWS Lambda (Node.js) generating presigned URL using @aws-sdk/signature-v4

Region: eu-west-2

Audio format: PCM16, 16 kHz, mono

Describe the bug

When connecting to AWS Transcribe using a presigned WebSocket URL, the connection closes immediately with no error message from the service.

Even when sending empty or valid PCM16 audio chunks, the socket disconnects right after onopen.
The client never receives transcription events.

Reproduction steps

1. Generate Presigned URL in Lambda

const request = new HttpRequest({
  protocol: "wss:",
  hostname: `transcribestreaming.${region}.amazonaws.com`,
  path: "/stream-transcription-websocket",
  method: "GET",
  query: {
    "language-code": "en-US",
    "media-encoding": "pcm",
    "sample-rate": "16000"
  },
  headers: {
    host: `transcribestreaming.${region}.amazonaws.com`,
  },
});

const signer = new SignatureV4({
  credentials: defaultProvider(),
  region,
  service: "transcribe",
  sha256: Sha256,
});

const signedRequest = await signer.presign(request, { expiresIn: 300 });
const url = `wss://${signedRequest.hostname}:8443${signedRequest.path}?${queryStr}`;

2. Connect From Frontend

const socket = new WebSocket(wsUrl);

socket.onopen = () => {
  console.log("✅ Connected to Transcribe");
};

socket.onmessage = (event) => {
  console.log("Message:", event.data);
};

socket.onerror = (event) => {
  console.error("WebSocket error:", event);
};

socket.onclose = (event) => {
  console.log("🔴 Disconnected", event.code, event.reason, event.wasClean);
};

3. Send Audio

processor.onaudioprocess = (e) => {
  if (socket.readyState !== 1) return;

  const float32Array = e.inputBuffer.getChannelData(0);
  const int16Array = new Int16Array(float32Array.length);
  for (let i = 0; i < float32Array.length; i++) {
    int16Array[i] = Math.max(-32768, Math.min(32767, Math.floor(float32Array[i] * 32768)));
  }

  const audioEvent = {
    headers: {
      ":content-type": { type: "string", value: "application/octet-stream" },
      ":event-type": { type: "string", value: "AudioEvent" },
      ":message-type": { type: "string", value: "event" },
    },
    body: new Uint8Array(int16Array.buffer),
  };

  socket.send(marshaller.marshall(audioEvent));
};

⚠️ Observed Behavior

  • WebSocket connects (onopen fires ✅).
  • Immediately disconnects, with no transcription events.
  • onclose shows no reason (event.reason empty).
  • Adding silent/empty audio does not change behavior.

✅ Expected Behavior

  • WebSocket should remain open until closed by client.
  • Transcribe should return transcription events or at least an error message when audio is invalid.
  • Empty audio should result in clean closure, not silent disconnect.

❓ Questions for AWS Team

  1. Is there an issue with how the presigned URL is generated (path, port :8443, or query params)?
  2. Does Transcribe WebSocket require a special initial handshake message before sending audio chunks?
  3. What are the exact requirements for the AudioEvent payload format (PCM16, marshalling, headers)?
  4. Why does the connection close without returning an error or BadRequestException?

Would you like me to also include a Minimal Working Example repo structure (with Lambda + React frontend code) in this issue so AWS support can reproduce it in one go?

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions