janhq/jan · error · Error
No active session found for model: ${modelId}
Error message
No active session found for model: ${modelId} What it means
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.
Source
Thrown at extensions/llamacpp-extension/src/index.ts:3534
if (!result.success) {
logger.warn(
`Pre-eviction of ${victim} reported failure: ${result.error}`
)
} else {
logger.info(
`Pre-evicted chat model ${victim} to make room for ${incomingModelId}`
)
}
} catch (e) {
logger.warn(`Pre-eviction of ${victim} threw:`, e)
}
}
}
override async unload(modelId: string): Promise<UnloadResult> {
const sInfo = await this.findSessionByModel(modelId)
if (!sInfo) {
throw new Error(`No active session found for model: ${modelId}`)
}
try {
const result = await unloadLlamaModel(modelId)
if (result.success) {
this.loadedChatOrder = this.loadedChatOrder.filter((m) => m !== modelId)
logger.info(`Successfully unloaded model ${modelId}`)
} else {
logger.warn(`Failed to unload model ${modelId}: ${result.error}`)
}
return result
} catch (error) {
logger.error('Error in unload command:', error)
return {
success: false,
error: `Failed to unload model: ${error}`,
}
}
}View on GitHub (pinned to fad3f12a14)
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.
Example fix
// before
await provider.unload('qwen') // throws if not loaded
// after
const loaded = await provider.getLoadedModels()
if (loaded.includes('qwen')) await provider.unload('qwen')
// or swallow the not-loaded case:
try { await provider.unload('qwen') }
catch (e) { if (!/No active session/.test(String(e))) throw e } Defensive patterns
Strategy: validation
Validate before calling
// Only unload if actually loaded; treat missing as success
const loaded = await provider.getLoadedModels()
if (!loaded.includes(modelId)) {
return // nothing to do - already gone
}
await provider.unload(modelId) Type guard
async function isModelLoaded(provider: { getLoadedModels(): Promise<string[]> }, id: string): Promise<boolean> {
return (await provider.getLoadedModels()).includes(id)
} Try / catch
try { await provider.unload(modelId) }
catch (e) {
if (/No active session/.test(String(e))) return // already gone - fine
throw e
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Model already loaded!!
- Model ${modelId} does not exist
- No active MLX session found for model: ${modelId}
- Model with ID ${model.id} already exists
- Invalid modelId: ${modelId}. Only alphanumeric and / _ - . c
AI-assisted analysis of janhq/jan@fad3f12a14 (2026-08-12).
Data as JSON: /api/errors/e42cec2df835b933.
Report an issue: GitHub.