{"record":{"id":"a56ed43a57ae17b9","repo":"affaan-m/ECC","slug":"unknown-model-model-r-allowed-allowed-models","errorCode":null,"errorMessage":"Unknown model: {model!r}. Allowed: {ALLOWED_MODELS}","messagePattern":"Unknown model: (.+?)\\. Allowed: (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"skills/skill-comply/scripts/runner.py","lineNumber":44,"sourceCode":"SHELL_BUILTINS = frozenset({\"cd\", \"pushd\", \"popd\"})\n\n\n@dataclass(frozen=True)\nclass ScenarioRun:\n    scenario: Scenario\n    observations: tuple[ObservationEvent, ...]\n    sandbox_dir: Path\n\n\ndef run_scenario(\n    scenario: Scenario,\n    model: str = \"sonnet\",\n    max_turns: int = 30,\n    timeout: int = 300,\n) -> ScenarioRun:\n    \"\"\"Execute a scenario and extract tool calls from stream-json output.\"\"\"\n    if model not in ALLOWED_MODELS:\n        raise ValueError(f\"Unknown model: {model!r}. Allowed: {ALLOWED_MODELS}\")\n\n    sandbox_dir = _safe_sandbox_dir(scenario.id)\n    _setup_sandbox(sandbox_dir, scenario)\n\n    result = subprocess.run(\n        [\n            \"claude\", \"-p\", scenario.prompt,\n            \"--model\", model,\n            \"--max-turns\", str(max_turns),\n            \"--add-dir\", str(sandbox_dir),\n            \"--allowedTools\", \"Read,Write,Edit,Bash,Glob,Grep\",\n            \"--output-format\", \"stream-json\",\n            \"--verbose\",\n        ],\n        capture_output=True,\n        text=True,\n        timeout=timeout,\n        cwd=sandbox_dir,","sourceCodeStart":26,"sourceCodeEnd":62,"githubUrl":"https://github.com/affaan-m/ECC/blob/01e15490f04e29cfefe3896951f43db46994d8ee/skills/skill-comply/scripts/runner.py#L26-L62","documentation":"run_scenario() validates the model argument against ALLOWED_MODELS = {'haiku','sonnet','opus'} before invoking claude -p. Any other string (including a full model ID like 'claude-3-5-sonnet-20241022', or a typo like 'Sonnet') is rejected up front.","triggerScenarios":"Passing model='claude-3-5-sonnet', model='Sonnet', model='gpt-4', or any alias not in the frozenset to run_scenario().","commonSituations":"Reusing a model identifier from another tool's config; passing a full Anthropic model ID where the CLI expects a short alias; copy-pasting a config that worked against a different harness.","solutions":["Pass one of the allowed short aliases: 'haiku', 'sonnet', or 'opus'.","If you need a newer alias added, extend ALLOWED_MODELS in runner.py and document it.","Do not pass full model IDs — claude -p resolves the alias itself."],"exampleFix":"# before\nrun = run_scenario(scenario, model='claude-3-5-sonnet-20241022')\n\n# after\nrun = run_scenario(scenario, model='sonnet')","handlingStrategy":"validation","validationCode":"from scripts.runner import ALLOWED_MODELS\n\ndef safe_model(model: str) -> str:\n    if model not in ALLOWED_MODELS:\n        raise SystemExit(f'Unknown model {model!r}; allowed: {sorted(ALLOWED_MODELS)}')\n    return model","typeGuard":"from scripts.runner import ALLOWED_MODELS\n\ndef is_allowed_model(model: object) -> bool:\n    return isinstance(model, str) and model in ALLOWED_MODELS","tryCatchPattern":"from scripts.runner import run_scenario\ntry:\n    run = run_scenario(scenario, model=user_model)\nexcept ValueError as e:\n    if 'Unknown model' in str(e):\n        user_model = 'sonnet'\n        run = run_scenario(scenario, model=user_model)\n    else:\n        raise","preventionTips":["Restrict the model input in your CLI with argparse choices=sorted(ALLOWED_MODELS).","Document the short-alias convention (haiku/sonnet/opus) wherever claude -p is invoked.","Never accept arbitrary model strings from untrusted config — validate against the allowlist."],"tags":["validation","claude-cli","skill-comply","runner"],"backgroundTag":null,"analyzedSha":"01e15490f04e29cfefe3896951f43db46994d8ee","analyzedAt":"2026-08-13T00:31:08.655Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}