{"record":{"id":"cd70bac89930d56a","repo":"vllm-project/vllm","slug":"models-url-did-not-return-a-model-list","errorCode":null,"errorMessage":"{models_url} did not return a model list.","messagePattern":"(.+?) did not return a model list\\.","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"tools/recipes/recipe_json_to_vllm_config.py","lineNumber":338,"sourceCode":"            return f\"{strategy} (recommended)\"\n        return strategy\n\n    selected = choose_from_menu(strategies, strategy_label, \"Select strategy: \")\n    return selected, sources[selected]\n\n\ndef discover_recipe_source(\n    api_base: str,\n    requested_model: str | None,\n    requested_hardware: str | None,\n    requested_strategy: str | None,\n) -> str:\n    print(\"No recipe JSON supplied; starting Recipes API discovery.\")\n\n    models_url = api_url(api_base, \"/models.json\")\n    models_data = load_json(models_url)\n    if not isinstance(models_data, list):\n        raise ValueError(f\"{models_url} did not return a model list.\")\n\n    models = [model for model in models_data if isinstance(model, dict)]\n    model = select_model(models, requested_model)\n\n    model_json_path = model.get(\"json\")\n    if not isinstance(model_json_path, str) or not model_json_path:\n        raise ValueError(f\"Selected model {model.get('hf_id')!r} has no JSON API path.\")\n\n    model_json_url = api_url(api_base, model_json_path)\n    model_data = load_json(model_json_url)\n    if not isinstance(model_data, dict):\n        raise ValueError(f\"{model_json_url} did not return a JSON object.\")\n\n    recommended = model_data.get(\"recommended_command\")\n    if not isinstance(recommended, dict):\n        raise ValueError(\n            f\"Model {model.get('hf_id')!r} has no rendered \"\n            \"recommended_command in the Recipes API.\"","sourceCodeStart":320,"sourceCodeEnd":356,"githubUrl":"https://github.com/vllm-project/vllm/blob/c794754062d49a8fdb63ab3c5215b488b865030c/tools/recipes/recipe_json_to_vllm_config.py#L320-L356","documentation":"During Recipes API discovery, the tool fetches {api_base}/models.json and expects a JSON array of model objects. If the response is anything else (an error object, an HTML error page parsed as JSON, a schema change), it raises ValueError naming the URL that misbehaved.","triggerScenarios":"The Recipes API (default https://recipes.vllm.ai) returning a JSON object/error payload instead of a list at /models.json; a custom --api-base that serves a different schema; a proxy or captive portal returning HTML that json.load() happens to accept as a dict.","commonSituations":"Recipes API outage or format migration; pointing --api-base at an internal mirror that does not implement /models.json; network middleboxes intercepting the request.","solutions":["Check the URL from the message in a browser/curl: `curl -sL <models_url> | head -c 300` and confirm it returns a JSON array.","If the API is temporarily broken, retry later or pin a known-good api base.","Bypass discovery entirely by passing a recipe JSON URL/file directly."],"exampleFix":"# before\npython tools/recipes/recipe_json_to_vllm_config.py\n# -> ValueError: https://recipes.vllm.ai/models.json did not return a model list.\n\n# after\ncurl -sL https://recipes.vllm.ai/models.json | head -c 200   # verify array output\npython tools/recipes/recipe_json_to_vllm_config.py https://recipes.vllm.ai/<model>/<hw>/<recipe>.json","handlingStrategy":"retry","validationCode":"import json, urllib.request\ndata = json.load(urllib.request.urlopen(models_url, timeout=30))\nif not isinstance(data, list):\n    raise SystemExit(f\"{models_url} returned {type(data).__name__}, expected a list; API unhealthy or wrong --api-base\")","typeGuard":"def is_model_list(data: object) -> bool:\n    return isinstance(data, list) and all(isinstance(m, dict) for m in data)","tryCatchPattern":"for attempt in range(3):\n    try:\n        data = load_json(models_url)\n        break\n    except (urllib.error.URLError, json.JSONDecodeError) as e:\n        if attempt == 2:\n            raise SystemExit(f\"Recipes API unreachable: {e}; pass a recipe JSON file instead\") from e","preventionTips":["Add a retry with backoff around Recipes API fetches in automation.","Pin a recipe JSON URL/file in CI so builds do not depend on live API discovery.","Smoke-test `curl -sL <api_base>/models.json` returns a JSON array after API changes."],"tags":["network","recipes","api","json","tooling"],"backgroundTag":null,"analyzedSha":"c794754062d49a8fdb63ab3c5215b488b865030c","analyzedAt":"2026-08-14T21:17:39.825Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}