tobi/qmd · error · SessionReleasedError

Session aborted

Error message

Session aborted

What it means

A queued session operation began executing after AbortController.abort() was already called. The wrapper re-checks the abort signal before invoking fn() and surfaces the abort reason (defaulting to 'Session aborted') as a SessionReleasedError.

Source

Thrown at src/llm.ts:2033

    }

    this.abortController.abort(new Error("Session released"));
    this.manager.release();
  }

  /**
   * Wrap an operation with tracking and abort checking.
   */
  private async withOperation<T>(fn: () => Promise<T>): Promise<T> {
    if (!this.isValid) {
      throw new SessionReleasedError();
    }

    this.manager.operationStart();
    try {
      // Check abort before starting
      if (this.abortController.signal.aborted) {
        throw new SessionReleasedError(
          this.abortController.signal.reason?.message || "Session aborted"
        );
      }
      return await fn();
    } finally {
      this.manager.operationEnd();
    }
  }

  async embed(text: string, options?: EmbedOptions): Promise<EmbeddingResult | null> {
    return this.withOperation(() => this.manager.getLlamaCpp().embed(text, options));
  }

  async embedBatch(texts: string[], options?: EmbedOptions): Promise<(EmbeddingResult | null)[]> {
    return this.withOperation(() => this.manager.getLlamaCpp().embedBatch(texts, options));
  }

  async expandQuery(

View on GitHub (pinned to dbfd0b4736)

Solutions

  1. Check session liveness (not aborted/disposed) before submitting work and re-create the session if aborted
  2. Await or cancel pending operations before calling abort()/close()
  3. Propagate the abort reason and retry the operation on a fresh session

Example fix

// before
const r = await session.run(fn); // may already be aborted
// after
if (session.abortController.signal.aborted) await recreateSession();
const r = await session.run(fn);
Defensive patterns

Strategy: retry

Validate before calling

if (session.abortController.signal.aborted) await recreateSession();

Type guard

const isSessionUsable = (s: Session) => !s.abortController.signal.aborted;

Try / catch

try { return await session.run(fn); } catch (e) { if (e instanceof SessionReleasedError) { await recreateSession(); return session.run(fn); } throw e; }

Prevention

When it happens

Trigger: Calling a session operation (embed, generate, rerank) after session.close()/dispose() or explicit abort(); or a queued operation whose signal was aborted while it waited in the queue.

Common situations: Timeout logic that aborts the session while requests are still queued; concurrent callers racing with shutdown; idle-unload of models aborting in-flight queued work.


AI-assisted analysis of tobi/qmd@dbfd0b4736 (2026-08-28). Data as JSON: /api/errors/c599bb2eb07e23b6. Report an issue: GitHub.