{"record":{"id":"c37add70301e6126","repo":"sgl-project/sglang","slug":"external-ngram-corpus-max-tokens-must-be-positive","errorCode":null,"errorMessage":"External ngram corpus max tokens must be positive.","messagePattern":"External ngram corpus max tokens must be positive\\.","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"python/sglang/srt/speculative/cpp_ngram/external_corpus.py","lineNumber":22,"sourceCode":"\n# Must match SuffixAutomaton::kSeparatorToken in suffix_automaton.h.\nSEPARATOR_TOKEN = -(2**31)\n\n# Default chunk size for streaming tokenized documents into the SAM.\nDEFAULT_CHUNK_SIZE = 4096\n\n\ndef iter_external_corpus_chunks(\n    path: str, tokenizer, max_tokens: int, chunk_size: int = DEFAULT_CHUNK_SIZE\n) -> Iterator[list[int]]:\n    \"\"\"Chunk documents and yield fixed-size token chunks from a JSONL corpus file.\"\"\"\n    corpus_path = Path(path)\n    if not corpus_path.is_file():\n        raise ValueError(f\"External ngram corpus path does not exist: {path}\")\n    if tokenizer is None:\n        raise ValueError(\"A tokenizer is required to load an external ngram corpus.\")\n    if max_tokens <= 0:\n        raise ValueError(\"External ngram corpus max tokens must be positive.\")\n\n    total_tokens = 0\n    has_previous_doc = False\n    with corpus_path.open(\"r\", encoding=\"utf-8\") as f:\n        for line_no, line in enumerate(f, start=1):\n            if not line.strip():\n                continue\n\n            try:\n                record = json.loads(line)\n            except json.JSONDecodeError as e:\n                raise ValueError(\n                    f\"Invalid JSON in external ngram corpus at line {line_no}: {e.msg}\"\n                ) from e\n\n            if not isinstance(record, str):\n                raise ValueError(\n                    \"Invalid external ngram corpus record at line \"","sourceCodeStart":4,"sourceCodeEnd":40,"githubUrl":"https://github.com/sgl-project/sglang/blob/0132848349585cfe6aae51c4941cbae872505f8a/python/sglang/srt/speculative/cpp_ngram/external_corpus.py#L4-L40","documentation":"The external ngram corpus loader enforces a positive token budget: max_tokens must be > 0 because it bounds how many tokens can be inserted into the ngram matcher's global budget. A zero or negative value fails fast with this ValueError.","triggerScenarios":"Calling add_external_corpus / iter_external_corpus_chunks with max_tokens=0 or a negative number, e.g. from an unset config default or a computed budget that underflowed to 0.","commonSituations":"Remaining-budget arithmetic returning 0 (all budget consumed by earlier corpora) and being forwarded as the new corpus's limit; a CLI/config value of 0 intended to mean 'unlimited'.","solutions":["Pass a positive max_tokens (e.g. the corpus manager's remaining_token_budget if > 0)","If 0 was meant as unlimited, pick an explicit large limit instead","Check remaining_token_budget() before adding another corpus"],"exampleFix":"# before\nadd_external_corpus(path, tok, max_tokens=0)\n# after\nbudget = corpus_manager.remaining_token_budget()\nif budget <= 0:\n    corpus_manager.remove_external_corpus(\"old\")\n    budget = corpus_manager.remaining_token_budget()\nadd_external_corpus(path, tok, max_tokens=budget)","handlingStrategy":"validation","validationCode":"if max_tokens <= 0:\n    raise ValueError(\"max_tokens must be positive\")\nadd_external_corpus(path, tok, max_tokens=max_tokens)","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Never forward computed budgets without a positivity check","Treat 0 configs as errors, not 'unlimited'"],"tags":["sglang","ngram","validation","invalid-argument"],"backgroundTag":"invalid-argument-value","analyzedSha":"0132848349585cfe6aae51c4941cbae872505f8a","analyzedAt":"2026-08-28T05:10:05.995Z","schemaVersion":2},"datasetVersion":"2026-08-28T06:17:29.519Z"}