{"record":{"id":"c66a80bdb2cee336","repo":"janhq/jan","slug":"model-already-loaded-c66a80","errorCode":null,"errorMessage":"Model already loaded!","messagePattern":"Model already loaded!","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"extensions/mlx-extension/src/index.ts","lineNumber":216,"sourceCode":"\n  private async getRandomPort(): Promise<number> {\n    try {\n      return await invoke<number>('plugin:mlx|get_mlx_random_port')\n    } catch {\n      logger.error('Unable to find a suitable port for MLX server')\n      throw new Error('Unable to find a suitable port for MLX model')\n    }\n  }\n\n  override async load(\n    modelId: string,\n    overrideSettings?: any,\n    isEmbedding: boolean = false,\n    bypassAutoUnload: boolean = false\n  ): Promise<SessionInfo> {\n    const sInfo = await this.findSessionByModel(modelId)\n    if (sInfo) {\n      throw new Error('Model already loaded!')\n    }\n\n    if (this.loadingModels.has(modelId)) {\n      return this.loadingModels.get(modelId)!\n    }\n\n    const loadingPromise = this.performLoad(\n      modelId,\n      overrideSettings,\n      isEmbedding,\n      bypassAutoUnload\n    )\n    this.loadingModels.set(modelId, loadingPromise)\n\n    try {\n      return await loadingPromise\n    } finally {\n      this.loadingModels.delete(modelId)","sourceCodeStart":198,"sourceCodeEnd":234,"githubUrl":"https://github.com/janhq/jan/blob/fad3f12a147d138388a66f0d92a02b2675f65294/extensions/mlx-extension/src/index.ts#L198-L234","documentation":"Thrown by load() when findSessionByModel(modelId) already returns a session, meaning the model is currently loaded and serving. Loading it again would start a second server process on a new port and double GPU memory use, so the extension refuses. A separate loadingModels map handles concurrent in-flight loads.","triggerScenarios":"Calling engine.load(modelId) twice without an intervening unload(); a chat thread already loaded the model and a second UI action triggers another load; the session registry was not cleared after a crash so findSessionByModel returns a stale entry.","commonSituations":"Race between two 'start chat' actions; model auto-loaded on app start and a manual load is also issued; a previous crash left a zombie session entry that findSessionByModel still finds.","solutions":["Check engine.getLoadedModels()/findSessionByModel before calling load, and reuse the existing session.","Call engine.unload(modelId) first if you genuinely need a fresh load with new settings.","If the entry is stale (process actually dead), run unload to clear it, then load."],"exampleFix":"// before\nawait engine.load(modelId, overrideSettings)\n\n// after\nconst existing = await engine.findSessionByModel(modelId)\nif (existing) {\n  // reuse, or unload first if overrideSettings require a reload\n  return existing\n}\nawait engine.load(modelId, overrideSettings)","handlingStrategy":"validation","validationCode":"const existing = await engine.findSessionByModel(modelId).catch(() => null)\nif (existing) {\n  // already loaded; reuse or unload first\n  return existing\n}\nawait engine.load(modelId)","typeGuard":null,"tryCatchPattern":"try {\n  await engine.load(modelId, overrideSettings)\n} catch (e) {\n  if (/Model already loaded/.test(String(e))) {\n    return // acceptable: nothing to do\n  }\n  throw e\n}","preventionTips":["Check findSessionByModel/getLoadedModels before calling load.","Debounce UI actions that trigger load to prevent races.","Call unload before re-loading with new settings."],"tags":["session","duplicate-load","validation","mlx","typescript"],"backgroundTag":null,"analyzedSha":"fad3f12a147d138388a66f0d92a02b2675f65294","analyzedAt":"2026-08-12T20:33:47.516Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}