{"record":{"id":"f1ac2e6d7c75698a","repo":"Hmbown/CodeWhale","slug":"provider-invocations-returned-no-providers","errorCode":null,"errorMessage":"provider!() invocations returned no providers","messagePattern":"provider!\\(\\) invocations returned no providers","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"scripts/check-provider-registry.py","lineNumber":171,"sourceCode":"        (\"Openmodel\", \"openmodel\"),\n        (\"MinimaxAnthropic\", \"minimax-anthropic\"),\n        (\"OpencodeZen\", \"opencode-zen\"),\n        # Alibaba Model Studio ships four plan/dialect identities, each with a\n        # hand-written impl Provider for the same reason as the rows above:\n        # the wire policy is not fixed, so provider!() cannot express them.\n        (\"ModelstudioTokenPlan\", \"modelstudio-token-plan\"),\n        (\"ModelstudioTokenPlanAnthropic\", \"modelstudio-token-plan-anthropic\"),\n        (\"ModelstudioCodingPlan\", \"modelstudio-coding-plan\"),\n        (\"ModelstudioCodingPlanAnthropic\", \"modelstudio-coding-plan-anthropic\"),\n    ]:\n        match = re.search(\n            rf'impl\\s+Provider\\s+for\\s+{variant_name}.*?fn\\s+id.*?\\\"({id_literal})\\\"',\n            provider_rs, re.DOTALL,\n        )\n        if match:\n            ids[variant_name] = match.group(1)\n    if not ids:\n        raise ValueError(\"provider!() invocations returned no providers\")\n    return ids\n\n\ndef api_provider_ids(tui_config_rs: str) -> dict[str, str]:\n    # ApiProvider ids derive from ProviderKind ids (via delegation to .kind().as_str())\n    # plus the legacy \"deepseek-cn\" variant that exists only in ApiProvider.\n    variant_to_id = provider_kind_ids(\"\")\n    # ApiProvider::SiliconflowCn maps to ProviderKind::SiliconflowCN\n    if \"SiliconflowCN\" in variant_to_id:\n        variant_to_id[\"SiliconflowCn\"] = variant_to_id[\"SiliconflowCN\"]\n    variant_to_id[\"DeepseekCN\"] = \"deepseek-cn\"\n    return variant_to_id\n\n\ndef provider_tables(config_rs: str) -> set[str]:\n    struct_start = require_index(\n        config_rs, \"pub struct ProvidersToml\", \"crates/config/src/lib.rs\"\n    )","sourceCodeStart":153,"sourceCodeEnd":189,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/8880682c63083a91624de936797efa3ce9e498fd/scripts/check-provider-registry.py#L153-L189","documentation":"provider_kind_ids found zero provider identities: no provider!() invocations in crates/config/src/provider.rs matched the one-argument-per-line regex, and none of the hand-listed manual `impl Provider for ...` regexes matched either. This is a catastrophic parse failure of the provider registry source, not ordinary drift — the script can no longer extract any canonical IDs. The macro regex specifically requires a newline between each macro argument.","triggerScenarios":"The provider!() macro was renamed or its invocations were reformatted onto one line; crates/config/src/provider.rs was moved or drastically restructured; all providers switched to an impl style the checker's regexes do not cover.","commonSituations":"Bulk reformatting that collapses macro arguments onto one line; macro renames during refactors; path changes after module reshuffles.","solutions":["Check provider.rs macro layout — the parser needs each provider!() argument on its own line; restore that formatting (the repo's rustfmt config normally preserves it)","If the macro was renamed or restructured, update the regexes inside provider_kind_ids() in scripts/check-provider-registry.py","Confirm crates/config/src/provider.rs exists at the path constant PROVIDER_RS","For new manually-implemented providers, add (Variant, \"id\") pairs to the hardcoded list in provider_kind_ids()"],"exampleFix":"// before (crates/config/src/provider.rs) — args on one line\nprovider!(Anthropic, Deepseek, \"deepseek\", aliases: [\"deepseek\"]);\n// after — one argument per line, as the checker's regex requires\nprovider!(\n    Anthropic,\n    Deepseek,\n    \"deepseek\",\n    aliases: [\"deepseek\"],\n);","handlingStrategy":"try-catch","validationCode":"pairs = re.findall(r'provider!\\(\\s*\\n\\s*\\w+,\\s*\\n\\s*(\\w+),', provider_rs)\nassert pairs, 'macro shape no longer matches — update the checker regex'","typeGuard":null,"tryCatchPattern":"try:\n    ids = provider_kind_ids(config_rs)\nexcept ValueError as e:\n    audit_macro_layout(provider_rs); raise","preventionTips":["Keep provider!() arguments one per line (the repo rustfmt config preserves this)","Update the checker regexes when renaming the macro","Extend the manual-impl list when adding hand-written providers"],"tags":["python","rust","regex","provider-registry","static-analysis"],"backgroundTag":null,"analyzedSha":"8880682c63083a91624de936797efa3ce9e498fd","analyzedAt":"2026-08-16T11:31:27.956Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}