{"record":{"id":"71b45123bcaa9de6","repo":"sgl-project/sglang","slug":"pd-dcp-transfer-requires-decode-prefix-len-to-alig","errorCode":null,"errorMessage":"PD DCP transfer requires decode_prefix_len to align to the virtual DCP page size ({virtual_page_size}), got {decode_prefix_len}","messagePattern":"PD DCP transfer requires decode_prefix_len to align to the virtual DCP page size \\((.+?)\\), got (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"python/sglang/srt/disaggregation/common/utils.py","lineNumber":157,"sourceCode":"class DCPTokenTransferPlan:\n    src_token_indices: npt.NDArray[np.int64]\n    dst_token_indices: npt.NDArray[np.int64]\n\n\ndef build_dcp_token_transfer_plan(\n    src_page_indices: npt.NDArray[np.int32],\n    dst_page_indices: npt.NDArray[np.int32],\n    *,\n    physical_page_size: int,\n    dcp_size: int,\n    dcp_rank: int,\n    src_page_offset: int = 0,\n    decode_prefix_len: int = 0,\n    num_kv_tokens: Optional[int] = None,\n) -> DCPTokenTransferPlan:\n    virtual_page_size = physical_page_size * dcp_size\n    if decode_prefix_len % virtual_page_size != 0:\n        raise ValueError(\n            \"PD DCP transfer requires decode_prefix_len to align to the virtual \"\n            f\"DCP page size ({virtual_page_size}), got {decode_prefix_len}\"\n        )\n\n    src_pages = np.asarray(src_page_indices, dtype=np.int64)\n    dst_pages = np.asarray(dst_page_indices, dtype=np.int64)\n    source_capacity = src_pages.size * physical_page_size\n    if num_kv_tokens is None:\n        num_kv_tokens = source_capacity\n    if not 0 <= num_kv_tokens <= source_capacity:\n        raise ValueError(\n            \"num_kv_tokens must fit in the provided source pages, \"\n            f\"got tokens={num_kv_tokens}, capacity={source_capacity}\"\n        )\n    if src_pages.size == 0:\n        empty = np.empty((0,), dtype=np.int64)\n        return DCPTokenTransferPlan(empty, empty.copy())\n","sourceCodeStart":139,"sourceCodeEnd":175,"githubUrl":"https://github.com/sgl-project/sglang/blob/0132848349585cfe6aae51c4941cbae872505f8a/python/sglang/srt/disaggregation/common/utils.py#L139-L175","documentation":"build_dcp_token_transfer_plan maps token positions onto DCP virtual pages (physical_page_size * dcp_size). It requires decode_prefix_len to be a multiple of the virtual page size so token positions align to page boundaries on all DCP ranks; otherwise the rank mapping math would be wrong.","triggerScenarios":"Calling send_kvcache_dcp with a decode_prefix_len not divisible by virtual_page_size = physical_page_size * dcp_size — e.g. prefix 100 with page_size 16 and dcp_size 4 (virtual 64).","commonSituations":"Prefix caching returning a non-aligned prefix length; chunked prefill leaving a partial virtual page; mismatched page_size between the servers shifting the prefix boundary.","solutions":["Pad decode_prefix_len up to the next virtual page boundary before building the plan","Verify page_size and dcp_size on both sides so the virtual page size matches what produced the prefix","Ensure prefix cache entries only keep aligned lengths when DCP is in use"],"exampleFix":"# before\nplan = build_dcp_token_transfer_plan(\n    physical_page_size=page_size, dcp_size=dcp_size,\n    decode_prefix_len=100, ...)  # virtual page 64 -> raises\n# after\nvpage = page_size * dcp_size\ndecode_prefix_len = ((100 + vpage - 1) // vpage) * vpage\nplan = build_dcp_token_transfer_plan(\n    physical_page_size=page_size, dcp_size=dcp_size,\n    decode_prefix_len=decode_prefix_len, ...)","handlingStrategy":"validation","validationCode":"vpage = physical_page_size * dcp_size\nassert decode_prefix_len % vpage == 0, f'prefix {decode_prefix_len} not aligned to {vpage}'","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Pad prefixes to virtual page boundaries when DCP is enabled","Only cache aligned prefix lengths in DCP deployments"],"tags":["disaggregation","dcp","alignment","prefix-cache"],"backgroundTag":"alignment-validation-failed","analyzedSha":"0132848349585cfe6aae51c4941cbae872505f8a","analyzedAt":"2026-08-28T05:10:05.995Z","schemaVersion":2},"datasetVersion":"2026-08-28T06:17:29.519Z"}