{"record":{"id":"736ac7e5445afc43","repo":"HKUDS/Vibe-Trading","slug":"unknown-universe-v-r-expected-one-of-sorted-b","errorCode":null,"errorMessage":"unknown universe {v!r}; expected one of {sorted(_BENCH_UNIVERSES)}","messagePattern":"unknown universe (.+?); expected one of (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":422,"severity":"error","filePath":"agent/src/api/alpha_routes.py","lineNumber":173,"sourceCode":"    zoo: str = Field(..., min_length=1, max_length=64)\n    universe: str = Field(..., min_length=1, max_length=64)\n    period: str = Field(..., min_length=4, max_length=32)\n    top: int = Field(20, ge=1, le=500)\n\n    @field_validator(\"zoo\")\n    @classmethod\n    def _zoo_known(cls, v: str) -> str:\n        if v not in _VALID_ZOOS:\n            raise ValueError(\n                f\"unknown zoo {v!r}; expected one of {sorted(_VALID_ZOOS)}\"\n            )\n        return v\n\n    @field_validator(\"universe\")\n    @classmethod\n    def _universe_known(cls, v: str) -> str:\n        if v not in _BENCH_UNIVERSES:\n            raise ValueError(\n                f\"unknown universe {v!r}; expected one of {sorted(_BENCH_UNIVERSES)}\"\n            )\n        return v\n\n\nclass CompareRequest(BaseModel):\n    \"\"\"POST /alpha/compare body — a head-to-head of >= 2 named alphas.\"\"\"\n\n    alpha_ids: list[str] = Field(..., min_length=2, max_length=50)\n    universe: str = Field(..., min_length=1, max_length=64)\n    period: str = Field(..., min_length=4, max_length=32)\n    sort: str = Field(\"ir\", min_length=1, max_length=32)\n\n    @field_validator(\"alpha_ids\")\n    @classmethod\n    def _ids_well_formed(cls, v: list[str]) -> list[str]:\n        # De-duplicate (preserve order) and validate each id shape.\n        seen: set[str] = set()","sourceCodeStart":155,"sourceCodeEnd":191,"githubUrl":"https://github.com/HKUDS/Vibe-Trading/blob/80ffdda44c5c4db0dd84d70e051cca591cea67df/agent/src/api/alpha_routes.py#L155-L191","documentation":"After successfully importing the local connector adapter module, load_adapter verifies the read-only contract: the module must expose callable check_status(), get_account_snapshot(), and get_positions(). If any is missing or not callable, RuntimeError names the missing operation. This is the enforcement of the plugin scaffold contract for read-only connectors.","triggerScenarios":"A developer hand-edited a scaffolded connector and deleted or renamed one of the three required functions, or returned a non-callable attribute (e.g. assigned a dict to get_positions). Then any read call via _local_plugin_call raises this.","commonSituations":"Filling in the scaffold but forgetting one function, renaming get_account_snapshot to something else, defining them nested inside another function/class, or syntax that makes the attribute a variable instead of a function.","solutions":["Open the adapter module and add the missing function(s) with module-level def, exact names: check_status, get_account_snapshot, get_positions","If you renamed one, rename it back to the contract name","Compare with a freshly scaffolded connector (plugin_scaffold) to confirm the required module-level API"],"exampleFix":"# before: missing get_positions\ndef check_status(*, credentials, config): ...\ndef get_account_snapshot(*, credentials, config): ...\n\n# after: implement all three module-level callables\ndef check_status(*, credentials, config): ...\ndef get_account_snapshot(*, credentials, config): ...\ndef get_positions(*, credentials, config):\n    return {\"positions\": []}","handlingStrategy":"validation","validationCode":"import importlib.util\n\nREQUIRED_OPS = (\"check_status\", \"get_account_snapshot\", \"get_positions\")\n\ndef adapter_satisfies_contract(module_path: str) -> bool:\n    spec = importlib.util.spec_from_file_location(\"_probe\", module_path)\n    mod = importlib.util.module_from_spec(spec)\n    spec.loader.exec_module(mod)\n    return all(callable(getattr(mod, op, None)) for op in REQUIRED_OPS)","typeGuard":"def is_valid_adapter(module) -> bool:\n    return all(callable(getattr(module, op, None)) for op in (\"check_status\", \"get_account_snapshot\", \"get_positions\"))","tryCatchPattern":"try:\n    adapter = load_adapter(plugin)\nexcept RuntimeError as e:\n    if \"is missing\" in str(e):\n        # report which operation and point dev at the adapter file\n        log.error(\"adapter contract violation: %s\", e)\n    raise","preventionTips":["Always start from the scaffold so all three functions exist","Run a contract smoke test on the adapter before install_connector","Keep the three functions module-level and never rename them"],"tags":["plugin-system","contract","local-connector","trading"],"backgroundTag":"missing-interface-implementation","analyzedSha":"80ffdda44c5c4db0dd84d70e051cca591cea67df","analyzedAt":"2026-08-28T12:46:38.989Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}