{"record":{"id":"934fa29749fb2aff","repo":"vllm-project/vllm","slug":"cannot-merge-nested-option-join-path-r-pa","errorCode":null,"errorMessage":"Cannot merge nested option {'.'.join(path)!r}: {part!r} is already a scalar","messagePattern":"Cannot merge nested option (.+?): (.+?) is already a scalar","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"tools/recipes/recipe_json_to_vllm_config.py","lineNumber":425,"sourceCode":"\n\ndef is_option_token(token: str) -> bool:\n    if token.startswith(\"--\"):\n        return True\n    if token in SHORT_ALIASES or token == \"-O\":\n        return True\n    return token.startswith(\"-O\") and len(token) > 2\n\n\ndef merge_value(dst: dict[str, Any], path: list[str], value: Any) -> None:\n    \"\"\"Merge dotted CLI args into nested YAML dictionaries.\"\"\"\n    cur = dst\n    for part in path[:-1]:\n        existing = cur.get(part)\n        if existing is None:\n            cur[part] = {}\n        elif not isinstance(existing, dict):\n            raise ValueError(\n                f\"Cannot merge nested option {'.'.join(path)!r}: \"\n                f\"{part!r} is already a scalar\"\n            )\n        cur = cur[part]\n\n    leaf = path[-1]\n    if leaf not in cur:\n        cur[leaf] = value\n        return\n\n    # Repeated CLI option. Preserve all values.\n    old = cur[leaf]\n    if not isinstance(old, list):\n        old = [old]\n    if isinstance(value, list):\n        old.extend(value)\n    else:\n        old.append(value)","sourceCodeStart":407,"sourceCodeEnd":443,"githubUrl":"https://github.com/vllm-project/vllm/blob/c794754062d49a8fdb63ab3c5215b488b865030c/tools/recipes/recipe_json_to_vllm_config.py#L407-L443","documentation":"merge_value() builds a nested dict from dotted CLI options (e.g. --quantization.pratio=0.5). While walking each path segment it requires that intermediate segments be dicts; if an earlier option already stored a scalar at that segment (or YAML alias collision), merging a nested key under it raises this ValueError.","triggerScenarios":"A recipe argv containing both a scalar option and a nested option sharing a prefix, e.g. `--rope-scaling 1.0` followed by `--rope-scaling.rope-type linear`, or two Recipes-rendered options whose normalized keys collide after underscore->dash normalization.","commonSituations":"Recipes API starts emitting compound/dotted options that overlap with existing flat flags; hand-edited recipe JSON with conflicting options; normalization (underscores to dashes in the top-level key) accidentally mapping two distinct keys onto the same path.","solutions":["Inspect the recipe's argv (jq '.argv' recipe.json) and find the two options sharing the dotted prefix","Fix the source recipe: use the nested form consistently (--rope-scaling.rope-type plus --rope-scaling.factor) and drop the conflicting scalar form","If the collision comes from normalization, rename one option in the recipe JSON or patch SHORT_ALIASES/normalize_key handling locally","Report the offending argv to the Recipes maintainers if the recipe is server-rendered"],"exampleFix":"# before (conflicting scalar + nested forms in argv)\n[\"vllm\",\"serve\",\"m\",\"--rope-scaling\",\"1.0\",\"--rope-scaling.rope-type\",\"linear\"]\n# after (nested form only)\n[\"vllm\",\"serve\",\"m\",\"--rope-scaling.rope-type\",\"linear\",\"--rope-scaling.factor\",\"8.0\"]","handlingStrategy":"validation","validationCode":"def check_no_prefix_collisions(argv: list[str]) -> None:\n    keys = set()\n    for t in argv:\n        if t.startswith(\"--\") and \"=\" in t:\n            keys.add(t[2:].split(\"=\", 1)[0])\n    for k in keys:\n        for other in keys:\n            if other != k and other.startswith(k + \".\"):\n                raise SystemExit(f\"option prefix collision: {k} vs {other}\")","typeGuard":"def option_is_mergeable(existing: object) -> bool:\n    return existing is None or isinstance(existing, dict)","tryCatchPattern":null,"preventionTips":["Use one form per option: either the scalar flag or the dotted nested keys, never both","When generating recipe argv programmatically, group dotted options under a single parent key"],"tags":["vllm-recipes","argv-parsing","config-merge"],"backgroundTag":null,"analyzedSha":"c794754062d49a8fdb63ab3c5215b488b865030c","analyzedAt":"2026-08-14T21:17:39.825Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}