{"record":{"id":"305c127d27b88d20","repo":"agalwood/Motrix","slug":"enginestartfailed-305c12","errorCode":"EngineStartFailed","errorMessage":"Engine recovery is unavailable before the first start attempt","messagePattern":"Engine recovery is unavailable before the first start attempt","errorType":"error_code","errorClass":"AppError","httpStatus":null,"severity":"error","filePath":"src/core/engine/engine-supervisor.ts","lineNumber":537,"sourceCode":"        isCurrent: defaultRpcIsCurrent,\n        available: defaultRpcAvailable,\n        process: defaultRpcProcess,\n        canRestore:\n          !defaultRpcIsCurrent &&\n          (defaultRpcAvailable || defaultRpcRequiresTermination),\n        requiresTermination: defaultRpcRequiresTermination,\n      },\n      suggestedRpcPort,\n      canRetry,\n      canForceTerminate,\n      canSwitchPort: canSwitchPort && suggestedRpcPort !== null,\n      recommendation,\n    }\n  }\n\n  async recover(request: EngineRecoveryRequest): Promise<EngineRecoveryResult> {\n    if (!this.binaryPath) {\n      throw new AppError(\n        ErrorCode.EngineStartFailed,\n        'Engine recovery is unavailable before the first start attempt'\n      )\n    }\n\n    const previousRpcPort = this.settingsManager.getEngine().rpcPort\n\n    if (\n      request.action === EngineRecoveryAction.RestoreDefaultPort &&\n      previousRpcPort === ENGINE_RPC_PORT\n    ) {\n      return {\n        ok: this.state === EngineState.Ready,\n        previousRpcPort,\n        rpcPort: previousRpcPort,\n        status: this.getStatus(),\n      }\n    }","sourceCodeStart":519,"sourceCodeEnd":555,"githubUrl":"https://github.com/agalwood/Motrix/blob/1a708ee57746c434e2c67a44bbf0906a976afea4/src/core/engine/engine-supervisor.ts#L519-L555","documentation":"Thrown as an AppError (code EngineStartFailed) by EngineSupervisor.recover when this.binaryPath is falsy — meaning start() has never been successfully called (or was never attempted), so the supervisor has no binary path to recover with. Recovery actions (retry, force-terminate, switch-port, restore-default-port) all require a known binary path and prior start context. This is a lifecycle/ordering guard.","triggerScenarios":"engineSupervisor.recover(request) is called before engineSupervisor.start() has been called at least once; binaryPath is set only inside start(), so it remains null/undefined until then.","commonSituations":"A UI or controller calls recover() during early initialization before the engine start sequence; a recovery flow is triggered by a stale event after the supervisor was reset; the supervisor was constructed but start() failed before setting binaryPath; a race between construction and the first start attempt.","solutions":["Call engineSupervisor.start() first and wait for it to attempt (success or failure) before calling recover()","Guard the recover() call with a check that the engine has been started at least once (check getStatus().state !== initial)","If start() never ran, trigger a fresh start instead of a recovery"],"exampleFix":"// before\nawait supervisor.recover({ action: EngineRecoveryAction.Retry }) // throws if start() never called\n// after\nawait supervisor.start()\n// ... if start fails ...\nawait supervisor.recover({ action: EngineRecoveryAction.Retry })","handlingStrategy":"validation","validationCode":"function canRecover(supervisor: EngineSupervisor): boolean {\n  return supervisor.getStatus().state !== EngineState.Stopped || /* has been started */ hasBeenStarted\n}\n// Simpler: track whether start() was called\nlet hasStarted = false\nawait supervisor.start()\nhasStarted = true\nif (!hasStarted) {\n  throw new Error('Call start() before recover()')\n}","typeGuard":null,"tryCatchPattern":"try {\n  await supervisor.recover(request)\n} catch (e) {\n  if (e instanceof AppError && e.code === ErrorCode.EngineStartFailed && e.message.includes('before the first start')) {\n    await supervisor.start() // fresh start instead of recovery\n  } else throw e\n}","preventionTips":["Always call start() before recover() — recovery requires prior start context","Gate the recovery UI on engine state — only offer recovery after a start attempt has been made","Track whether start() has been called in the controller layer"],"tags":["engine","supervisor","lifecycle","recovery","ordering"],"backgroundTag":null,"analyzedSha":"1a708ee57746c434e2c67a44bbf0906a976afea4","analyzedAt":"2026-08-12T16:18:09.346Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}