{"record":{"id":"5117c2cbd7c31eba","repo":"vllm-project/vllm","slug":"hardware-recipe-json-alternatives-must-be-an-obj","errorCode":null,"errorMessage":"Hardware recipe JSON `alternatives` must be an object when present.","messagePattern":"Hardware recipe JSON `alternatives` must be an object when present\\.","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"tools/recipes/recipe_json_to_vllm_config.py","lineNumber":276,"sourceCode":"\n    print(\"\\nAvailable hardware:\")\n    selected = choose_from_menu(hardware_ids, lambda value: value, \"Select hardware: \")\n    return selected, by_hardware[selected]\n\n\ndef strategy_sources(\n    api_base: str, hardware_json_url: str, recipe: dict[str, Any]\n) -> tuple[str, dict[str, str]]:\n    recommended = recipe.get(\"strategy\")\n    if not isinstance(recommended, str) or not recommended:\n        raise ValueError(\n            \"Hardware recipe JSON does not contain a usable `strategy` field.\"\n        )\n\n    sources = {recommended: hardware_json_url}\n    raw_alternatives = recipe.get(\"alternatives\") or {}\n    if not isinstance(raw_alternatives, dict):\n        raise ValueError(\n            \"Hardware recipe JSON `alternatives` must be an object when present.\"\n        )\n\n    for strategy, path in raw_alternatives.items():\n        if isinstance(strategy, str) and strategy and isinstance(path, str) and path:\n            sources[strategy] = api_url(api_base, path)\n\n    return recommended, sources\n\n\ndef select_strategy(\n    api_base: str,\n    hardware_json_url: str,\n    recipe: dict[str, Any],\n    requested: str | None,\n    interactive: bool,\n) -> tuple[str, str]:\n    recommended, sources = strategy_sources(api_base, hardware_json_url, recipe)","sourceCodeStart":258,"sourceCodeEnd":294,"githubUrl":"https://github.com/vllm-project/vllm/blob/c794754062d49a8fdb63ab3c5215b488b865030c/tools/recipes/recipe_json_to_vllm_config.py#L258-L294","documentation":"In the same recipe JSON, the optional 'alternatives' field must be a JSON object mapping strategy names to paths. strategy_sources() raises this ValueError when 'alternatives' is present but not a dict (e.g. a list or string).","triggerScenarios":"Hand-editing a recipe JSON so 'alternatives' becomes an array or scalar; merging recipe files with a tool that changed the value's type.","commonSituations":"Custom recipe authoring; schema drift between recipe file versions.","solutions":["Change 'alternatives' to an object: {\"<strategy-id>\": \"<path-or-url>\", ...} or remove it entirely (it is optional).","Validate the file with a JSON schema check before passing it to the tool."],"exampleFix":"# before\n\"alternatives\": [\"tp8\", \"tp4-pp2\"]\n# -> ValueError: Hardware recipe JSON `alternatives` must be an object when present.\n\n# after\n\"alternatives\": {\"tp8\": \"tp8.json\", \"tp4-pp2\": \"tp4-pp2.json\"}","handlingStrategy":"type-guard","validationCode":"alts = recipe.get(\"alternatives\")\nif alts is not None and not isinstance(alts, dict):\n    raise SystemExit(\"'alternatives' must be an object mapping strategy -> path\")","typeGuard":"def has_valid_alternatives(recipe: dict) -> bool:\n    alts = recipe.get(\"alternatives\")\n    return alts is None or (\n        isinstance(alts, dict)\n        and all(isinstance(k, str) and isinstance(v, str) for k, v in alts.items())\n    )","tryCatchPattern":null,"preventionTips":["Keep 'alternatives' an object of string->string pairs or omit it.","Lint recipe JSON with a schema checker in CI for custom recipe files."],"tags":["cli","recipes","json","validation","tooling"],"backgroundTag":null,"analyzedSha":"c794754062d49a8fdb63ab3c5215b488b865030c","analyzedAt":"2026-08-14T21:17:39.825Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}