{"record":{"id":"60baabc7285e7d20","repo":"hcengineering/platform","slug":"recorder-video-recording-is-not-supported","errorCode":null,"errorMessage":"Recorder: video recording is not supported","messagePattern":"Recorder: video recording is not supported","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"plugins/recorder-resources/src/recorder.ts","lineNumber":44,"sourceCode":"}\n\nexport class Recorder {\n  readonly mimeType: string\n  private readonly mediaRecorder: MediaRecorder\n  private readonly chunkStream: ChunkReader\n  private readonly chunkInterval: number\n  // Total elapsed recording time in milliseconds (excluding pauses)\n  private elapsedMs: number = 0\n  // Timestamp when recording or resuming started; null if paused or not started\n  private lastStartTime: number | null = null\n\n  constructor (\n    private readonly mediaStream: MediaStream,\n    readonly options: RecorderOptions\n  ) {\n    const supportedTypes = getSupportedMimeTypes()\n    if (supportedTypes.length === 0) {\n      throw new Error('Recorder: video recording is not supported')\n    }\n\n    this.mimeType = supportedTypes[0]\n    this.chunkInterval = options.chunkIntervalMs ?? chunkIntervalMs\n    this.chunkStream = new ChunkReader()\n\n    this.mediaRecorder = new MediaRecorder(mediaStream, {\n      mimeType: this.mimeType,\n      audioBitsPerSecond: options.audioBps,\n      videoBitsPerSecond: options.videoBps\n    })\n\n    console.debug('Recorder: MediaRecorder using', this.mimeType)\n\n    this.mediaRecorder.ondataavailable = async (e) => {\n      const kb = Math.floor(e.data.size / 1024)\n      console.debug('Recorder: MediaRecorder data available', formatElapsedTime(e.timecode), `${kb}KB`)\n      this.chunkStream.push(e.data)","sourceCodeStart":26,"sourceCodeEnd":62,"githubUrl":"https://github.com/hcengineering/platform/blob/63e28dc96483967b2fc21c881b3f1023c1de7718/plugins/recorder-resources/src/recorder.ts#L26-L62","documentation":"The Recorder constructor probes the browser via getSupportedMimeTypes() (checking MediaRecorder.isTypeSupported) for a video container/codec the current engine can record. If none is supported it throws before constructing the MediaRecorder, because recording cannot proceed without a viable mimeType. This is an upfront capability check, not a runtime recording failure.","triggerScenarios":"Constructing new Recorder(mediaStream, options) in a browser or context where no supported video MIME type exists: MediaRecorder API missing or isTypeSupported returns false for all candidates (e.g. unsupported browsers like older Safari/Firefox without codecs, webviews with MediaRecorder stubbed out, secure-context issues).","commonSituations":"Older Safari/iOS webviews lacking MediaRecorder video support; non-HTTPS pages where some APIs are restricted; headless/embedded browsers without codec licenses (no H.264/VP8); enterprise browsers with media APIs disabled by policy.","solutions":["Feature-detect before constructing: check typeof MediaRecorder !== 'undefined' and that some expected mimeType is supported; show a 'recording not supported' UI state instead of crashing.","Use an up-to-date Chromium/Firefox/Safari version with MediaRecorder video codec support.","Serve the app over HTTPS (secure context) so media APIs are fully enabled.","In unsupported environments, fall back to screen-capture/extension-based recording or an external recording service."],"exampleFix":"// before\nconst recorder = new Recorder(mediaStream, options) // throws if unsupported\n\n// after\nfunction recorderSupported(): boolean {\n  return (\n    typeof MediaRecorder !== 'undefined' &&\n    getSupportedMimeTypes().length > 0\n  )\n}\nif (!recorderSupported()) {\n  ui.showRecordingUnsupportedMessage()\n} else {\n  const recorder = new Recorder(mediaStream, options)\n}","handlingStrategy":"validation","validationCode":"function canRecordVideo(): boolean {\n  if (typeof MediaRecorder === 'undefined') return false\n  const candidates = ['video/webm;codecs=vp9', 'video/webm;codecs=vp8', 'video/webm', 'video/mp4']\n  return candidates.some((t) => MediaRecorder.isTypeSupported(t))\n}\nif (!canRecordVideo()) showRecordingUnsupportedNotice()","typeGuard":"function isRecorderSupported(): boolean {\n  return typeof MediaRecorder !== 'undefined' && typeof MediaRecorder.isTypeSupported === 'function'\n}","tryCatchPattern":"let recorder: Recorder | null = null\ntry {\n  recorder = new Recorder(mediaStream, options)\n} catch (err) {\n  if (err instanceof Error && err.message === 'Recorder: video recording is not supported') {\n    recorder = null\n    ui.showUnsupportedBrowserDialog()\n  } else {\n    throw err\n  }\n}","preventionTips":["Feature-detect MediaRecorder and a supported mimeType before offering the record button","Test recording in all supported browsers, including Safari/iOS webviews","Serve the app over HTTPS so media APIs are available in secure contexts","Provide a documented fallback path (external recorder or audio-only) for unsupported engines"],"tags":["browser","media-recorder","feature-detection","compatibility"],"backgroundTag":"mediarecorder-unsupported","analyzedSha":"63e28dc96483967b2fc21c881b3f1023c1de7718","analyzedAt":"2026-08-29T15:21:27.377Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}