hcengineering/platform · error

No video tracks found

Error message

No video tracks found

What it means

`getVideoDimensions(stream)` resolves video width/height from the stream's video track settings. When the settings report zero width or height, it falls back to loading the stream into a temporary `<video>` element and reading `videoWidth`/`videoHeight`. If that fallback still yields zero dimensions, it throws 'No video tracks found'. Despite the message, the real problem here is that video track settings and metadata loading both produced no usable dimensions — the stream has video tracks but they carry no resolvable size (e.g. track ended or settings unavailable).

Source

Thrown at plugins/recorder-resources/src/utils.ts:120

      await new Promise((resolve) => {
        const video = document.createElement('video')
        video.srcObject = stream
        video.onloadedmetadata = () => {
          maxWidth = video.videoWidth
          maxHeight = video.videoHeight
          video.remove()
          resolve(null)
        }
        video.play().catch(() => {
          // Ignore play errors, just resolve
          video.remove()
          resolve(null)
        })
      })
    }

    if (maxWidth === 0 || maxHeight === 0) {
      throw new Error('No video tracks found')
    }

    return { width: maxWidth, height: maxHeight }
  }

  throw new Error('No video tracks found')
}

export function whenStreamEnded (stream: MediaStream, callback: () => void): void {
  const tracks = stream.getTracks()

  const cleanup = (): void => {
    callback()

    for (const track of tracks) {
      track.onended = null
    }
  }

View on GitHub (pinned to 63e28dc964)

Solutions

  1. Ensure the stream has live, enabled video tracks before calling: check `track.readyState === 'live'`.
  2. Call `getVideoDimensions` soon after the stream is obtained, before any track could end.
  3. Await any stream setup (e.g. `video.play()` promises) in the surrounding code so metadata is ready.
  4. Wrap the call in try/catch and fall back to a default resolution (e.g. 1280x720) for layout purposes.
  5. On Firefox specifically, retry the metadata load once with a user-gesture-driven play if autoplay was blocked.

Example fix

// before
const { width, height } = await getVideoDimensions(oldStream)

// after
const tracks = oldStream.getVideoTracks().filter(t => t.readyState === 'live')
if (tracks.length === 0) return { width: 1280, height: 720 }
const { width, height } = await getVideoDimensions(new MediaStream(tracks)).catch(() => ({ width: 1280, height: 720 }))
Defensive patterns

Strategy: fallback

Validate before calling

const liveVideoTracks = stream.getVideoTracks().filter(t => t.readyState === 'live')
if (liveVideoTracks.length === 0) return { width: 1280, height: 720 }

Type guard

function hasLiveVideoTrack(stream: MediaStream): boolean {
  return stream.getVideoTracks().some(t => t.readyState === 'live')
}

Try / catch

let dims
try {
  dims = await getVideoDimensions(stream)
} catch (err) {
  if (err instanceof Error && err.message === 'No video tracks found') {
    dims = { width: 1280, height: 720 } // layout fallback
  } else throw err
}

Prevention

When it happens

Trigger: Passing a stream whose video tracks exist but report `width`/`height` of 0 or undefined AND whose `<video>` metadata load yields 0 (e.g. tracks already ended/muted, or the video element failed to load metadata before resolving); calling on a stream after its tracks were stopped.

Common situations: Firefox tracks missing width/height in `getSettings()` combined with `video.play()` being blocked or metadata not loaded; measuring a canvas/screen stream after the share was cancelled; capturing dimensions from a stale stream stored before a device switch.


AI-assisted analysis of hcengineering/platform@63e28dc964 (2026-08-29). Data as JSON: /api/errors/6b06315469767dae. Report an issue: GitHub.