unslothai/unsloth · error · ResearchConflictError

This thread already has a Deep Research run

Error message

This thread already has a Deep Research run

What it means

ResearchConflictError raised while creating a Deep Research run: inserting into research_thread_claims hit sqlite3.IntegrityError, and a follow-up SELECT confirmed a claim row already exists for that thread_id — the table enforces one active research run per thread (unique thread_id). The insert+check pattern distinguishes this from an unrelated integrity failure (which re-raises bare).

Source

Thrown at studio/backend/storage/research_runs_db.py:158

    created_at: int | None = None,
) -> dict:
    created = created_at or now_ms()
    conn = get_connection()
    try:
        conn.execute("BEGIN IMMEDIATE")
        try:
            conn.execute(
                "INSERT INTO research_thread_claims (owner_subject, thread_id, created_at) "
                "VALUES (?, ?, ?)",
                (owner_subject, thread_id, created),
            )
        except sqlite3.IntegrityError as exc:
            claim = conn.execute(
                "SELECT 1 FROM research_thread_claims WHERE thread_id=?",
                (thread_id,),
            ).fetchone()
            if claim is not None:
                raise ResearchConflictError("This thread already has a Deep Research run") from exc
            raise
        if assistant_message_id:
            message = conn.execute(
                "SELECT * FROM chat_messages WHERE id=?", (assistant_message_id,)
            ).fetchone()
            metadata = {
                "researchRunId": run_id,
                "researchStatus": "planning",
                "researchPlanRevision": 0,
                "serverManaged": True,
            }
            if message is None:
                conn.execute(
                    """INSERT INTO chat_messages
                       (id, thread_id, parent_id, role, content_json, metadata_json, created_at)
                       VALUES (?, ?, ?, 'assistant', '[]', ?, ?)""",
                    (
                        assistant_message_id,

View on GitHub (pinned to 203007d190)

Solutions

  1. Wait for the existing run to finish (or cancel it) before starting another in the same thread.
  2. Disable the Start button while a run is active; key it off the thread's researchStatus metadata.
  3. On catching this conflict, re-fetch the thread state and surface the existing run instead of erroring to the user.

Example fix

// before
button.onclick = () => api.startResearch(threadId); // double-click => conflict

// after
if (thread.researchStatus && !isTerminal(thread.researchStatus)) return;
button.disabled = true;
try { await api.startResearch(threadId); } finally { button.disabled = false; }
Defensive patterns

Strategy: validation

Validate before calling

// Only start a run when the thread has none active
const status = thread.metadata?.researchStatus;
const terminal = ['completed', 'failed', 'cancelled'].includes(status);
if (status && !terminal) throw new ConflictError('This thread already has a Deep Research run');
await api.startResearch(threadId);

Type guard

function threadHasActiveRun(t: Thread): boolean {
  const s = t.metadata?.researchStatus;
  return !!s && !['completed', 'failed', 'cancelled'].includes(s);
}

Try / catch

try { await createRun(threadId, ...); }
catch (e) {
  if (e?.name === 'ResearchConflictError' || /already has a Deep Research run/.test(e?.message)) {
    const state = await fetchThreadState(threadId);  // adopt the live run
    showExistingRun(state.activeRun);
  } else throw e;
}

Prevention

When it happens

Trigger: Two 'start research' requests for the same chat thread racing (double-click, retry, or two tabs); starting a new run while a previous run is still active/unfinished in that thread; a client that does not wait for the terminal state before issuing the next run.

Common situations: Double-submit from a flaky network retry; UI lets the user click Start again before the run reaches a terminal state; background poller that re-triggers creation.

Related errors


AI-assisted analysis of unslothai/unsloth@203007d190 (2026-08-15). Data as JSON: /api/errors/9ee31c61934efbcc. Report an issue: GitHub.