{"record":{"id":"f9b7a67b93b52ab5","repo":"stablyai/orca","slug":"simulator-stream-must-use-http-or-https","errorCode":null,"errorMessage":"Simulator stream must use http or https.","messagePattern":"Simulator stream must use http or https\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/main/emulator/mjpeg-frame-stream.ts","lineNumber":18,"sourceCode":"import { request as httpRequest, type ClientRequest, type IncomingMessage } from 'node:http'\nimport { request as httpsRequest } from 'node:https'\nimport { 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      },","sourceCodeStart":1,"sourceCodeEnd":36,"githubUrl":"https://github.com/stablyai/orca/blob/1136503c6a231a16dce8f921f6fadb63d181e8db/src/main/emulator/mjpeg-frame-stream.ts#L1-L36","documentation":"normalizeStreamUrl rejects any URL whose protocol is not http: or https:. MjpegFrameStream only speaks HTTP (it dispatches via node:http/https), so a ws://, file://, or other scheme would break the request constructor.","triggerScenarios":"Constructing `new MjpegFrameStream(streamUrl, ...)` where `new URL(streamUrl).protocol` is anything except http/https (mjpeg-frame-stream.ts:16-18).","commonSituations":"Passing the session's wsUrl (ws://...) where streamUrl is expected; a copy-paste of the websocket endpoint; a serve-sim output schema change returning a non-http URL field.","solutions":["Pass the `streamUrl` field of EmulatorSessionInfo, not `wsUrl`.","Confirm parseServeSimDetachedSession populated streamUrl from an http(s) source.","Normalize/validate the URL scheme before constructing the stream."],"exampleFix":"// before\nnew MjpegFrameStream(session.wsUrl, cb) // ws://...\n\n// after\nnew MjpegFrameStream(session.streamUrl, cb) // http://.../stream.mjpeg","handlingStrategy":"validation","validationCode":"const u = new URL(streamUrl)\nif (u.protocol !== 'http:' && u.protocol !== 'https:') { throw new Error('pass session.streamUrl (http(s))') }","typeGuard":"function isStreamProtocolError(e: unknown): boolean {\n  return e instanceof Error && /must use http or https/.test(e.message)\n}","tryCatchPattern":"try { new MjpegFrameStream(url, cb) }\ncatch (e) { if (isStreamProtocolError(e)) { url = session.streamUrl; new MjpegFrameStream(url, cb) } else throw e }","preventionTips":["Always pass EmulatorSessionInfo.streamUrl, never wsUrl.","Validate the scheme before constructing the stream.","Treat a non-http stream URL from serve-sim as a parse bug."],"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"}