hcengineering/platform · error
Cannot stop from state: ${state}
Error message
Cannot stop from state: ${state} What it means
`stop()` may only run while the recorder is 'recording' or 'paused'. This error is thrown when `recorder.stop()` is called from any other state ('idle', 'ready', 'stopping', 'stopped'), meaning there is no recording in progress to finalize. It exists so the recorder does not attempt to finalize a MediaRecorder that does not exist or is already being finalized.
Source
Thrown at plugins/recorder-resources/src/stores/recorder.ts:147
const { screenStream, micStream } = get(manager)
const canvasStream = composer.getStream()
const stream = combineStreams(canvasStream, micStream, screenStream)
// Create and start screen recorder
screenRecorder = await createScreenRecorder(stream)
await screenRecorder.start()
startElapsedTimer()
updateStore({ state: 'recording' })
},
async stop (): Promise<void> {
const { state } = get(store)
if (state !== 'recording' && state !== 'paused') {
throw new Error(`Cannot stop from state: ${state}`)
}
if (screenRecorder === null) {
throw new Error('No active recording')
}
updateStore({ state: 'stopping' })
stopElapsedTimer()
const result = await screenRecorder.stop()
updateStore({ state: 'stopped', result })
manager.cleanup()
composer.cleanup()
screenRecorder = null
},
View on GitHub (pinned to 63e28dc964)
Solutions
- Guard the call: only invoke `stop()` when the store's `state` is 'recording' or 'paused'.
- Keep a reference to the in-flight stop promise and reuse/await it instead of calling `stop()` again on double clicks.
- Use `recorder.cancel()` or `cleanup()` when you want to discard/reset from a non-active state instead of `stop()`.
- Disable the stop control unless `state === 'recording' || state === 'paused'`.
Example fix
// before
async function stopRecording () {
await recorder.stop()
}
// after
let stopping: Promise<void> | null = null
async function stopRecording () {
if (stopping !== null) return stopping
const { state } = getStore(recorder)
if (state !== 'recording' && state !== 'paused') return
stopping = recorder.stop().finally(() => { stopping = null })
return stopping
} Defensive patterns
Strategy: validation
Validate before calling
const { state } = get(recorder)
if (state !== 'recording' && state !== 'paused') return // nothing to stop
await recorder.stop() Type guard
function canStop(state: RecorderState): state is 'recording' | 'paused' {
return state === 'recording' || state === 'paused'
} Try / catch
try {
await recorder.stop()
} catch (err) {
if (err instanceof Error && err.message.startsWith('Cannot stop from state:')) {
// already stopped or never started — safe to ignore and reset UI
} else throw err
} Prevention
- Store the in-flight stop promise and return it for repeated stop requests (idempotent wrapper).
- Disable the stop button unless `state === 'recording' || state === 'paused'`.
- Coordinate the stream-ended auto-stop with manual stop so only one path calls `stop()`.
- Use `cancel()`/`cleanup()` for reset-from-any-state semantics instead of `stop()`.
When it happens
Trigger: Calling `recorder.stop()` twice (the second call sees 'stopped'); calling `stop()` before any recording started ('idle'/'ready'); calling `stop()` while a previous `stop()` is still awaiting `screenRecorder.stop()` ('stopping'); the stream-ended callback firing after `stop()` already transitioned the state.
Common situations: Double-click on a stop button; a UI calling stop both from a stream-ended event handler (`onStreamEnded`) and from a manual stop action; unmount cleanup code calling `stop()` after the user already stopped; tests tearing down while state is 'ready'.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- Cannot start recording from state: ${state}
- Cannot pause from state: ${state}
- Cannot resume from state: ${state}
- No active recording
- Delete allowed only for active workspaces
AI-assisted analysis of hcengineering/platform@63e28dc964 (2026-08-29).
Data as JSON: /api/errors/439c5827e916162d.
Report an issue: GitHub.