{"record":{"id":"b9822064f89b8942","repo":"hcengineering/platform","slug":"cannot-resume-from-state-state","errorCode":null,"errorMessage":"Cannot resume from state: ${state}","messagePattern":"Cannot resume from state: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"plugins/recorder-resources/src/stores/recorder.ts","lineNumber":185,"sourceCode":"      const { state } = get(store)\n      if (state !== 'recording') {\n        throw new Error(`Cannot pause from state: ${state}`)\n      }\n\n      if (screenRecorder === null) {\n        throw new Error('No active recording')\n      }\n\n      await screenRecorder.pause()\n      stopElapsedTimer()\n      updateStore({ state: 'paused' })\n    },\n\n    async resume (): Promise<void> {\n      const { state } = get(store)\n\n      if (state !== 'paused') {\n        throw new Error(`Cannot resume from state: ${state}`)\n      }\n\n      if (screenRecorder === null) {\n        throw new Error('No active recording')\n      }\n\n      await screenRecorder.resume()\n      startElapsedTimer()\n      updateStore({ state: 'recording' })\n    },\n\n    async cancel (): Promise<void> {\n      if (screenRecorder !== null) {\n        await screenRecorder?.cancel()\n        screenRecorder = null\n      }\n\n      const { result } = get(store)","sourceCodeStart":167,"sourceCodeEnd":203,"githubUrl":"https://github.com/hcengineering/platform/blob/63e28dc96483967b2fc21c881b3f1023c1de7718/plugins/recorder-resources/src/stores/recorder.ts#L167-L203","documentation":"`resume()` requires the recorder to be in exactly the 'paused' state. This error is thrown when resume is requested from 'idle', 'ready', 'recording', 'stopping', or 'stopped'. In particular, resuming a recorder that is already recording (not paused) is rejected — resume is not idempotent.","triggerScenarios":"Calling `recorder.resume()` while `state === 'recording'` (already running); calling `resume()` before any recording started; calling `resume()` after `stop()` completed ('stopped'); calling `resume()` while a stop is pending ('stopping').","commonSituations":"A play/resume button that doesn't distinguish 'recording' from 'paused'; double-invocation of a resume handler; flows that resume after the stream-ended callback already stopped the recording; tests resuming without a prior pause.","solutions":["Only call `resume()` when the store state is 'paused'; if `state === 'recording'`, do nothing.","Model the pause/resume control as a single toggle driven by the current state.","Disable the resume control unless `state === 'paused'`.","If the recording already ended, start a fresh session (`initialize` if needed, then `start()`) instead of resuming."],"exampleFix":"// before\nif (shouldContinue) void recorder.resume()\n\n// after\nconst { state } = getStore(recorder)\nif (state === 'paused' && shouldContinue) void recorder.resume()","handlingStrategy":"validation","validationCode":"const { state } = get(recorder)\nif (state === 'recording') return // already running, nothing to resume\nif (state !== 'paused') return\nawait recorder.resume()","typeGuard":"function canResume(state: RecorderState): state is 'paused' {\n  return state === 'paused'\n}","tryCatchPattern":"try {\n  await recorder.resume()\n} catch (err) {\n  if (err instanceof Error && err.message.startsWith('Cannot resume from state:')) {\n    // recording already running or finished; reconcile UI with store state\n  } else throw err\n}","preventionTips":["Only call `resume()` when store state is exactly 'paused'.","Treat resume as non-idempotent; guard repeated invocations.","Use a single toggle control that picks pause vs resume from state.","If state is 'stopped', start a new session instead of resuming."],"tags":["state-machine","recorder","resume","invalid-state-transition"],"backgroundTag":"invalid-state-transition","analyzedSha":"63e28dc96483967b2fc21c881b3f1023c1de7718","analyzedAt":"2026-08-29T15:21:27.377Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}