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
- Verify the corpus source actually contains tokens before finishing (check file size / token count)
- Fix the upstream read/filter pipeline so at least one non-empty token vector is appended
- 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
- Count tokens as you append; only finish when > 0
- Sanity-check corpus file size > 0 before loading
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
- External ngram corpus exceeds the remaining token budget ({m
- startExternalCorpusLoad called while another load is in prog
- appendExternalCorpusTokens called without startExternalCorpu
- finishExternalCorpusLoad called without startExternalCorpusL
- External corpus '${corpus_id}' already exists. Remove it bef
AI-assisted analysis of sgl-project/sglang@0132848349 (2026-08-28).
Data as JSON: /api/errors/fe8e34b02c3a417f.
Report an issue: GitHub.