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
- Ensure the stream has live, enabled video tracks before calling: check `track.readyState === 'live'`.
- Call `getVideoDimensions` soon after the stream is obtained, before any track could end.
- Await any stream setup (e.g. `video.play()` promises) in the surrounding code so metadata is ready.
- Wrap the call in try/catch and fall back to a default resolution (e.g. 1280x720) for layout purposes.
- 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
- Measure dimensions immediately after stream acquisition, before tracks can end.
- Filter for `readyState === 'live'` video tracks first.
- On Firefox, expect missing settings dimensions and rely on the metadata-load path; ensure `video.play()` isn't blocked by autoplay policy.
- Always define sensible default dimensions for UI layout as a fallback.
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.