janhq/jan · error · Error

Model already loaded!!

Error message

Model already loaded!!

What it means

Thrown by load() at the very top when findSessionByModel(modelId) returns a non-null SessionInfo - i.e. the llama.cpp router already reports an active session for this modelId. load() is designed to be idempotent-ish: a concurrent second load of the same model joins the in-flight promise (loadingModels map) rather than starting a duplicate, but a second load of an already-LOADED model is a hard error instead of a silent no-op, to surface UI/logic bugs that double-load.

Source

Thrown at extensions/llamacpp-extension/src/index.ts:3432

        if (
          cleanKey &&
          valueParts.length > 0 &&
          !cleanKey.startsWith('LLAMA')
        ) {
          target[cleanKey] = valueParts.join('=').trim()
        }
      })
  }

  override async load(
    modelId: string,
    _settings?: unknown,
    isEmbedding: boolean = false
  ): Promise<SessionInfo> {
    const sInfo = await this.findSessionByModel(modelId)
    if (sInfo) {
      throw new Error('Model already loaded!!')
    }

    if (this.loadingModels.has(modelId)) {
      return this.loadingModels.get(modelId)!
    }

    const loadingPromise = this.performLoad(modelId, isEmbedding)
    this.loadingModels.set(modelId, loadingPromise)

    try {
      return await loadingPromise
    } finally {
      this.loadingModels.delete(modelId)
    }
  }

  // Awaits the deferred startup, then makes one direct attempt if the router
  // still isn't up. Safe to call redundantly: startRouter reuses a router that

View on GitHub (pinned to fad3f12a14)

Solutions

  1. Before calling load, check if the model is already loaded (findSessionByModel or getLoadedModels) and skip or unload first.
  2. Call unload(modelId) before re-loading if you intend to reload with new settings.
  3. If you don't know the state, wrap load in a try/catch that, on 'Model already loaded', treats it as success (the model IS loaded).
  4. Audit the caller for duplicate load invocations triggered by UI event races.

Example fix

// before
await provider.load('qwen'); await provider.load('qwen') // second throws
// after - guard with state check
const loaded = await provider.getLoadedModels()
if (!loaded.includes('qwen')) await provider.load('qwen')
// or treat already-loaded as success:
try { await provider.load('qwen') }
catch (e) { if (!/already loaded/.test(String(e))) throw e }
Defensive patterns

Strategy: validation

Validate before calling

// Avoid double-load: only load if not already in a session
const loaded = await provider.getLoadedModels()
if (loaded.includes(modelId)) {
  // already loaded - treat as success, or unload first to force a reload
  return
}
await provider.load(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.load(modelId) }
catch (e) {
  if (/already loaded/.test(String(e))) return // model is loaded - fine
  throw e
}

Prevention

When it happens

Trigger: Calling load('qwen') twice in succession without an unload in between. Two UI actions (e.g. chat + embed bootstrap) both call load on the same id. The router was not aware the model was unloaded (stale session). Loading an embedding model that's already loaded as a chat model under the same id.

Common situations: App startup races between the embedder bootstrap and the user's first chat. Hot-reload during development re-invokes load. A 'switch model' flow forgot to unload the previous one. Router restart didn't clear the session table so findSessionByModel still returns the old session.

Related errors


AI-assisted analysis of janhq/jan@fad3f12a14 (2026-08-12). Data as JSON: /api/errors/fe53391a59e47dca. Report an issue: GitHub.