sgl-project/sglang · error · ValueError

External corpus '{corpus_id}' already exists. Remove it befo

Error message

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

What it means

Ngram_corpus tracks loaded external corpora by corpus_id in _corpus_token_counts. load_external_corpus_named refuses to overwrite: re-loading an id that already exists raises this ValueError, telling the caller to remove the old corpus first.

Source

Thrown at python/sglang/srt/speculative/cpp_ngram/ngram_corpus.py:66

            self._next_state_id += 1
            self._req_id_to_state_id[req_id] = sid
        return sid

    def batch_put(self, batch_tokens: List[List[int]]):
        self._obj.insert(batch_tokens)

    def synchronize(self):
        self._obj.synchronize()  # type: ignore

    @property
    def remaining_token_budget(self) -> int:
        return self.external_corpus_max_tokens - self._total_loaded_tokens

    def load_external_corpus_named(
        self, corpus_id: str, chunks: Iterable[Sequence[int]]
    ) -> int:
        if corpus_id in self._corpus_token_counts:
            raise ValueError(
                f"External corpus '{corpus_id}' already exists. Remove it before "
                f"adding a new corpus with the same id."
            )
        # Note(kpham-sgl): remaining_token_budget is stale (e.g if there are removes
        # during the load), which makes the budget more conservative than it should be.
        # This is acceptable because otherwise load_external_corpus_named would need to check the budget after each chunk,
        # which would be inefficient.
        _, loaded_token_count = self._obj.load_external_corpus_named(
            corpus_id, chunks, self.remaining_token_budget
        )
        return loaded_token_count

    # Commit corpus bookkeeping after successful load. Call only at background thread join.
    # (or after synchronous load_external_corpus_named returns)
    def commit_external_corpus_load(
        self, corpus_id: str, loaded_token_count: int
    ) -> None:
        self._corpus_token_counts[corpus_id] = loaded_token_count

View on GitHub (pinned to 0132848349)

Solutions

  1. Remove the existing corpus first: remove_external_corpus(corpus_id), then reload
  2. Or use a new unique corpus_id for the updated corpus

Example fix

# before
manager.add_external_corpus("docs", chunks)  # second time -> ValueError
# after
if "docs" in manager.list_external_corpora():
    manager.remove_external_corpus("docs")
manager.add_external_corpus("docs", chunks)
Defensive patterns

Strategy: validation

Validate before calling

if corpus_id in manager.list_external_corpora():  # or equivalent membership check
    manager.remove_external_corpus(corpus_id)
manager.load_external_corpus_named(corpus_id, chunks)

Try / catch

try:
    manager.load_external_corpus_named(cid, chunks)
except ValueError as e:
    if "already exists" in str(e):
        manager.remove_external_corpus(cid)
        manager.load_external_corpus_named(cid, chunks)
    else:
        raise

Prevention

When it happens

Trigger: Calling load_external_corpus_named (or add_external_corpus) twice with the same corpus_id without an intervening remove_external_corpus(corpus_id).

Common situations: Re-adding a corpus after editing its file (e.g. hot-reload of knowledge), retry logic that re-sends the same id, or duplicate ids from a config list.

Related errors


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