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

External corpus is empty — no tokens were loaded.

Error message

External corpus is empty — no tokens were loaded.

What it means

After finalization, if the staged suffix automaton contains zero tokens the load is considered failed and staging is reset. This catches corpora that were started but never received any tokens via appendExternalCorpusTokens.

Source

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

  }
  staging_sam_ = std::make_unique<SuffixAutomaton>();
}

void Ngram::appendExternalCorpusTokens(const std::vector<int32_t>& tokens) {
  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();

View on GitHub (pinned to 0132848349)

Solutions

  1. Verify the corpus source actually contains tokens before finishing (check file size / token count)
  2. Fix the upstream read/filter pipeline so at least one non-empty token vector is appended
  3. Log appended token counts during load and finish only after > 0

Example fix

// before
ngram.startExternalCorpusLoad();
// (no appends)
ngram.finishExternalCorpusLoad("wiki"); // throws
// after
ngram.startExternalCorpusLoad();
for (auto& chunk : read_chunks(path)) if (!chunk.empty()) ngram.appendExternalCorpusTokens(chunk);
ngram.finishExternalCorpusLoad("wiki");
Defensive patterns

Strategy: validation

Validate before calling

size_t appended = 0;
for (auto& chunk : chunks) { if (!chunk.empty()) { ngram.appendExternalCorpusTokens(chunk); appended += chunk.size(); } }
if (appended > 0) ngram.finishExternalCorpusLoad(id); else abort_load();

Try / catch

try { ngram.finishExternalCorpusLoad(id); } catch (const std::runtime_error& e) { /* empty corpus: check source file and retry after fixing */ }

Prevention

When it happens

Trigger: Calling startExternalCorpusLoad() followed by finishExternalCorpusLoad() with zero appendExternalCorpusTokens calls, or appends of only empty vectors.

Common situations: Empty corpus file / 0-byte download; a filtering step that dropped all tokens; chunked reader that read nothing (bad path, IO error swallowed); racing append thread not yet run.

Related errors


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