{"record":{"id":"603b869a48bcf76a","repo":"stablyai/orca","slug":"simulator-stream-must-target-stream-mjpeg","errorCode":null,"errorMessage":"Simulator stream must target stream.mjpeg.","messagePattern":"Simulator stream must target stream\\.mjpeg\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/main/emulator/mjpeg-frame-stream.ts","lineNumber":21,"sourceCode":"import { extractJpegFrames } from './mjpeg-frame-parser'\n\nconst RECONNECT_DELAY_MS = 1_000\nconst REQUEST_TIMEOUT_MS = 10_000\nconst MAX_FPS = 30\nconst MIN_FRAME_INTERVAL_MS = Math.floor(1_000 / MAX_FPS)\n\nexport type MjpegFrameStreamCallbacks = {\n  onError: (message: string) => void\n  onFrame: (frame: Buffer<ArrayBufferLike>) => void\n}\n\nfunction normalizeStreamUrl(streamUrl: string, streamKey?: string): URL {\n  const url = new URL(streamUrl)\n  if (url.protocol !== 'http:' && url.protocol !== 'https:') {\n    throw new Error('Simulator stream must use http or https.')\n  }\n  if (!url.pathname.endsWith('/stream.mjpeg')) {\n    throw new Error('Simulator stream must target stream.mjpeg.')\n  }\n  url.searchParams.set('raw', '1')\n  if (streamKey) {\n    url.searchParams.set('_orca', streamKey)\n  }\n  return url\n}\n\nfunction requestForUrl(url: URL, response: (res: IncomingMessage) => void): ClientRequest {\n  return (url.protocol === 'https:' ? httpsRequest : httpRequest)(\n    url,\n    {\n      headers: {\n        Accept: 'application/octet-stream, image/jpeg'\n      },\n      timeout: REQUEST_TIMEOUT_MS\n    },\n    response","sourceCodeStart":3,"sourceCodeEnd":39,"githubUrl":"https://github.com/stablyai/orca/blob/1136503c6a231a16dce8f921f6fadb63d181e8db/src/main/emulator/mjpeg-frame-stream.ts#L3-L39","documentation":"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.","triggerScenarios":"Constructing MjpegFrameStream with a URL whose pathname does not end in '/stream.mjpeg' (mjpeg-frame-stream.ts:19-21).","commonSituations":"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.","solutions":["Append `/stream.mjpeg` to the serve-sim base URL.","Use streamUrlFromServeSimUrl() to derive the correct path.","Use the session's pre-parsed `streamUrl` rather than constructing the URL by hand."],"exampleFix":"// before\nnew MjpegFrameStream('http://localhost:8080', cb)\n\n// after\nnew MjpegFrameStream('http://localhost:8080/stream.mjpeg', cb)","handlingStrategy":"validation","validationCode":"if (!streamUrl.endsWith('/stream.mjpeg')) { streamUrl = `${streamUrl.replace(/\\/$/, '')}/stream.mjpeg` }","typeGuard":"function isStreamPathError(e: unknown): boolean {\n  return e instanceof Error && /must target stream\\.mjpeg/.test(e.message)\n}","tryCatchPattern":"try { new MjpegFrameStream(url, cb) }\ncatch (e) { if (isStreamPathError(e)) { url = streamUrlFromServeSimUrl(url); new MjpegFrameStream(url, cb) } else throw e }","preventionTips":["Use the session's pre-parsed streamUrl.","Derive paths with streamUrlFromServeSimUrl() rather than string concatenation.","Never pass the /ax endpoint to the frame stream."],"tags":["mjpeg","stream","url","validation"],"backgroundTag":null,"analyzedSha":"1136503c6a231a16dce8f921f6fadb63d181e8db","analyzedAt":"2026-08-12T23:15:58.167Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}