sgl-project/sglang · error · std::runtime_error

External corpus '${corpus_id}' already exists. Remove it bef

Error message

External corpus '${corpus_id}' already exists. Remove it before adding a new corpus with the same id.

What it means

Corpora are stored per corpus_id in a map; installing a new corpus under an id that already exists is rejected to avoid silently replacing data. The staged automaton stays installed... actually staging was already moved; the throw happens under mutex before emplace.

Source

Thrown at python/sglang/kernels/jit/csrc/ngram_corpus/ngram.cpp:98

  if (!staging_sam_) {
    throw std::runtime_error("appendExternalCorpusTokens called without startExternalCorpusLoad");
  }
  staging_sam_->appendTokens(tokens);
}

void Ngram::finishExternalCorpusLoad(const std::string& corpus_id) {
  if (!staging_sam_) {
    throw std::runtime_error("finishExternalCorpusLoad called without startExternalCorpusLoad");
  }
  staging_sam_->finalize();
  if (staging_sam_->empty()) {
    staging_sam_.reset();
    throw std::runtime_error("External corpus is empty — no tokens were loaded.");
  }
  // Only lock briefly to install the completed SAM.
  std::unique_lock<std::mutex> lock(mutex_);
  if (sams_.find(corpus_id) != sams_.end()) {
    throw std::runtime_error(
        "External corpus '" + corpus_id + "' already exists. Remove it before adding a new corpus with the same id.");
  }
  sams_.emplace(corpus_id, std::move(staging_sam_));
}

void Ngram::removeExternalCorpus(const std::string& corpus_id) {
  std::unique_lock<std::mutex> lock(mutex_);
  sams_.erase(corpus_id);
}

void Ngram::resetStagingSam() {
  // staging_sam_ is only accessed from the loading thread — no lock needed.
  staging_sam_.reset();
}

void Ngram::clearExternalCorpus() {
  std::unique_lock<std::mutex> lock(mutex_);
  sams_.clear();

View on GitHub (pinned to 0132848349)

Solutions

  1. Call removeExternalCorpus(corpus_id) before loading the same id again
  2. Use a unique corpus id per load (e.g. versioned: 'wiki-v2')
  3. Check hasExternalCorpus(corpus_id)/equivalent API before finishing

Example fix

// before
ngram.startExternalCorpusLoad();
/* append */ ngram.finishExternalCorpusLoad("wiki"); // 'wiki' exists
// after
if (has_corpus("wiki")) ngram.removeExternalCorpus("wiki");
ngram.startExternalCorpusLoad();
/* append */ ngram.finishExternalCorpusLoad("wiki");
Defensive patterns

Strategy: try-catch

Validate before calling

// if an exists-check API is available:
if (ngram.hasExternalCorpus(id)) ngram.removeExternalCorpus(id);
ngram.finishExternalCorpusLoad(id);

Try / catch

try { ngram.finishExternalCorpusLoad(id); } catch (const std::runtime_error& e) {
  if (std::string(e.what()).find("already exists") != std::string::npos) { ngram.removeExternalCorpus(id); /* reload */ }
}

Prevention

When it happens

Trigger: finishExternalCorpusLoad(corpus_id) where corpus_id already has an entry in sams_ (a previous successful load with the same id).

Common situations: Re-running a corpus load script without removeExternalCorpus first; reloading/refreshing a corpus on a long-lived server; id collisions between datasets.

Related errors


AI-assisted analysis of sgl-project/sglang@0132848349 (2026-08-28). Data as JSON: /api/errors/c9d79bb880ab7211. Report an issue: GitHub.