{"record":{"id":"1d94ebc4ff5cfe90","repo":"calesthio/OpenMontage","slug":"model-must-be-one-of-sora-2-sora-2-pro","errorCode":null,"errorMessage":"model must be one of: sora-2, sora-2-pro","messagePattern":"model must be one of: sora-2, sora-2-pro","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"tools/video/sora_video.py","lineNumber":239,"sourceCode":"        if cls._version_tuple(getattr(openai, \"__version__\", \"\")) < _MIN_OPENAI_VERSION:\n            return False\n        return hasattr(OpenAI(), \"videos\")\n\n    @staticmethod\n    def _version_tuple(version: str) -> tuple[int, int, int]:\n        parts = []\n        for part in version.split(\".\")[:3]:\n            digits = \"\".join(ch for ch in part if ch.isdigit())\n            parts.append(int(digits or \"0\"))\n        while len(parts) < 3:\n            parts.append(0)\n        return tuple(parts)\n\n    @staticmethod\n    def _normalize_model(inputs: dict[str, Any]) -> str:\n        model = str(inputs.get(\"model\", _DEFAULT_MODEL)).strip().lower()\n        if model not in _ALLOWED_MODELS:\n            raise ValueError(\"model must be one of: sora-2, sora-2-pro\")\n        return model\n\n    @staticmethod\n    def _normalize_size(inputs: dict[str, Any], model: str) -> str:\n        default_size = \"1280x720\" if inputs.get(\"aspect_ratio\") == \"16:9\" else _DEFAULT_SIZE\n        size = str(inputs.get(\"size\", default_size)).strip().lower()\n        allowed = {\"1280x720\", \"720x1280\"} if model == \"sora-2\" else set(_ALLOWED_SIZES)\n        if size not in allowed:\n            raise ValueError(f\"size must be one of: {', '.join(sorted(allowed))} for model {model}\")\n        return size\n\n    @staticmethod\n    def _normalize_seconds(inputs: dict[str, Any]) -> str:\n        seconds = str(inputs.get(\"seconds\") or inputs.get(\"duration\") or _DEFAULT_SECONDS).strip().lower()\n        seconds = seconds[:-1] if seconds.endswith(\"s\") else seconds\n        if seconds not in _ALLOWED_SECONDS:\n            raise ValueError(\"seconds must be one of: 4, 8, 12\")\n        return seconds","sourceCodeStart":221,"sourceCodeEnd":257,"githubUrl":"https://github.com/calesthio/OpenMontage/blob/95e1c3d0ab93482159818560f6a8c8e866b9139f/tools/video/sora_video.py#L221-L257","documentation":"ValueError raised by SoraVideo._normalize_model when the 'model' input, after strip+lowercase, is not in _ALLOWED_MODELS. Only the two OpenAI video models 'sora-2' and 'sora-2-pro' are accepted; anything else — including plausible names like 'sora', 'sora-2-turbo', or other providers' model ids — is rejected before any API call.","triggerScenarios":"Passing model='sora', model='soravideo', model='dangao-2' (the Chinese-market alias), or leaving trailing junk like 'sora-2 '. (Note trailing whitespace alone is stripped, so ' Sora-2 ' is fine.)","commonSituations":"Model lists copy-pasted from news articles or other gateways (Azure OpenAI deployments use custom deployment names like 'my-sora-deployment'); version drift after new Sora releases when users guess at names.","solutions":["Use exactly 'sora-2' or 'sora-2-pro'.","If your deployment uses a custom name (Azure-style), remove the model override and let the default apply, or map your deployment name to one of the allowed values at your call site.","Omit model to fall back to _DEFAULT_MODEL."],"exampleFix":"# before\ninputs = {\"model\": \"sora-2-turbo\"}\n\n# after\ninputs = {\"model\": \"sora-2-pro\"}","handlingStrategy":"validation","validationCode":"ALLOWED = {\"sora-2\", \"sora-2-pro\"}\nmodel = str(inputs.get(\"model\", \"sora-2\")).strip().lower()\nif model not in ALLOWED:\n    inputs[\"model\"] = \"sora-2\"","typeGuard":"def is_valid_sora_model(v) -> bool:\n    return str(v).strip().lower() in {\"sora-2\", \"sora-2-pro\"}","tryCatchPattern":null,"preventionTips":["Source model names from a constant list shared with your UI/config, never free text.","Map external deployment names to the two allowed values at your boundary."],"tags":["validation","sora","openai","model-selection"],"backgroundTag":null,"analyzedSha":"95e1c3d0ab93482159818560f6a8c8e866b9139f","analyzedAt":"2026-08-15T06:31:20.014Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}