stablyai/orca · error

Simulator stream must target stream.mjpeg.

Error message

Simulator stream must target stream.mjpeg.

What it means

normalizeStreamUrl requires the pathname to end with /stream.mjpeg so the JPEG frame extractor receives MJPEG bytes rather than arbitrary content. A bare host:port or the /ax path would feed the wrong content-type to the parser.

Source

Thrown at src/main/emulator/mjpeg-frame-stream.ts:21

import { extractJpegFrames } from './mjpeg-frame-parser'

const RECONNECT_DELAY_MS = 1_000
const REQUEST_TIMEOUT_MS = 10_000
const MAX_FPS = 30
const MIN_FRAME_INTERVAL_MS = Math.floor(1_000 / MAX_FPS)

export type MjpegFrameStreamCallbacks = {
  onError: (message: string) => void
  onFrame: (frame: Buffer<ArrayBufferLike>) => void
}

function normalizeStreamUrl(streamUrl: string, streamKey?: string): URL {
  const url = new URL(streamUrl)
  if (url.protocol !== 'http:' && url.protocol !== 'https:') {
    throw new Error('Simulator stream must use http or https.')
  }
  if (!url.pathname.endsWith('/stream.mjpeg')) {
    throw new Error('Simulator stream must target stream.mjpeg.')
  }
  url.searchParams.set('raw', '1')
  if (streamKey) {
    url.searchParams.set('_orca', streamKey)
  }
  return url
}

function requestForUrl(url: URL, response: (res: IncomingMessage) => void): ClientRequest {
  return (url.protocol === 'https:' ? httpsRequest : httpRequest)(
    url,
    {
      headers: {
        Accept: 'application/octet-stream, image/jpeg'
      },
      timeout: REQUEST_TIMEOUT_MS
    },
    response

View on GitHub (pinned to 1136503c6a)

Solutions

  1. Append `/stream.mjpeg` to the serve-sim base URL.
  2. Use streamUrlFromServeSimUrl() to derive the correct path.
  3. Use the session's pre-parsed `streamUrl` rather than constructing the URL by hand.

Example fix

// before
new MjpegFrameStream('http://localhost:8080', cb)

// after
new MjpegFrameStream('http://localhost:8080/stream.mjpeg', cb)
Defensive patterns

Strategy: validation

Validate before calling

if (!streamUrl.endsWith('/stream.mjpeg')) { streamUrl = `${streamUrl.replace(/\/$/, '')}/stream.mjpeg` }

Type guard

function isStreamPathError(e: unknown): boolean {
  return e instanceof Error && /must target stream\.mjpeg/.test(e.message)
}

Try / catch

try { new MjpegFrameStream(url, cb) }
catch (e) { if (isStreamPathError(e)) { url = streamUrlFromServeSimUrl(url); new MjpegFrameStream(url, cb) } else throw e }

Prevention

When it happens

Trigger: Constructing MjpegFrameStream with a URL whose pathname does not end in '/stream.mjpeg' (mjpeg-frame-stream.ts:19-21).

Common situations: Passing the serve-sim base URL (http://host:port) without the suffix; passing the /ax endpoint by mistake; manually building the URL instead of using the parsed session field.

Related errors


AI-assisted analysis of stablyai/orca@1136503c6a (2026-08-12). Data as JSON: /api/errors/603b869a48bcf76a. Report an issue: GitHub.