{"record":{"id":"e42cec2df835b933","repo":"janhq/jan","slug":"no-active-session-found-for-model-modelid","errorCode":null,"errorMessage":"No active session found for model: ${modelId}","messagePattern":"No active session found for model: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"extensions/llamacpp-extension/src/index.ts","lineNumber":3534,"sourceCode":"        if (!result.success) {\n          logger.warn(\n            `Pre-eviction of ${victim} reported failure: ${result.error}`\n          )\n        } else {\n          logger.info(\n            `Pre-evicted chat model ${victim} to make room for ${incomingModelId}`\n          )\n        }\n      } catch (e) {\n        logger.warn(`Pre-eviction of ${victim} threw:`, e)\n      }\n    }\n  }\n\n  override async unload(modelId: string): Promise<UnloadResult> {\n    const sInfo = await this.findSessionByModel(modelId)\n    if (!sInfo) {\n      throw new Error(`No active session found for model: ${modelId}`)\n    }\n    try {\n      const result = await unloadLlamaModel(modelId)\n      if (result.success) {\n        this.loadedChatOrder = this.loadedChatOrder.filter((m) => m !== modelId)\n        logger.info(`Successfully unloaded model ${modelId}`)\n      } else {\n        logger.warn(`Failed to unload model ${modelId}: ${result.error}`)\n      }\n      return result\n    } catch (error) {\n      logger.error('Error in unload command:', error)\n      return {\n        success: false,\n        error: `Failed to unload model: ${error}`,\n      }\n    }\n  }","sourceCodeStart":3516,"sourceCodeEnd":3552,"githubUrl":"https://github.com/janhq/jan/blob/fad3f12a147d138388a66f0d92a02b2675f65294/extensions/llamacpp-extension/src/index.ts#L3516-L3552","documentation":"Thrown by unload() when findSessionByModel(modelId) returns null - the llama.cpp router has no active session for that id, so there is nothing to unload. unload is intentionally strict rather than idempotent so callers detect when their bookkeeping (which models they think are loaded) diverges from the router's actual session table.","triggerScenarios":"Calling unload on a model that was never loaded; calling unload after the model already crashed/evicted internally (evictChatIfAtCapacity removed it); the router restarted and lost its session table; calling unload on the wrong id (typo / case mismatch); double-unload by two UI actions.","commonSituations":"Capacity eviction silently removed a model and the UI still shows it as loaded; user clicks unload on a model that errored during load. Router restart wiped sessions but the UI list was not refreshed. Embedding-model vs chat-model id confusion.","solutions":["Before calling unload, check getLoadedModels()/findSessionByModel and skip if not present.","Treat 'No active session' as a no-op success if your flow only wants the model gone (it already is).","After a router restart, refresh your loaded-models state before issuing unload calls.","Verify the modelId casing/spelling matches the id used at load time."],"exampleFix":"// before\nawait provider.unload('qwen') // throws if not loaded\n// after\nconst loaded = await provider.getLoadedModels()\nif (loaded.includes('qwen')) await provider.unload('qwen')\n// or swallow the not-loaded case:\ntry { await provider.unload('qwen') }\ncatch (e) { if (!/No active session/.test(String(e))) throw e }","handlingStrategy":"validation","validationCode":"// Only unload if actually loaded; treat missing as success\nconst loaded = await provider.getLoadedModels()\nif (!loaded.includes(modelId)) {\n  return // nothing to do - already gone\n}\nawait provider.unload(modelId)","typeGuard":"async function isModelLoaded(provider: { getLoadedModels(): Promise<string[]> }, id: string): Promise<boolean> {\n  return (await provider.getLoadedModels()).includes(id)\n}","tryCatchPattern":"try { await provider.unload(modelId) }\ncatch (e) {\n  if (/No active session/.test(String(e))) return // already gone - fine\n  throw e\n}","preventionTips":["Refresh the loaded-models state after capacity eviction and after router restarts.","Make unload idempotent at the caller by pre-checking the loaded list.","Track model lifecycle centrally instead of having multiple UI actions call unload independently."],"tags":["model","unload","session","state","validation"],"backgroundTag":null,"analyzedSha":"fad3f12a147d138388a66f0d92a02b2675f65294","analyzedAt":"2026-08-12T20:33:47.516Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}