{"record":{"id":"e1c1e440d0a2ab93","repo":"github/spec-kit","slug":"providers-i-must-be-a-json-object","errorCode":null,"errorMessage":"providers[{i}]: must be a JSON object","messagePattern":"providers\\[(.+?)\\]: must be a JSON object","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"src/specify_cli/authentication/config.py","lineNumber":127,"sourceCode":"                    UserWarning,\n                    stacklevel=2,\n                )\n        except OSError:\n            pass  # stat failed — skip permission check\n\n    raw = json.loads(config_path.read_text(encoding=\"utf-8\"))\n\n    if not isinstance(raw, dict):\n        raise ValueError(f\"auth.json must be a JSON object, got {type(raw).__name__}\")\n\n    providers_raw = raw.get(\"providers\")\n    if not isinstance(providers_raw, list):\n        raise ValueError(\"auth.json must contain a 'providers' array\")\n\n    entries: list[AuthConfigEntry] = []\n    for i, entry_raw in enumerate(providers_raw):\n        if not isinstance(entry_raw, dict):\n            raise ValueError(f\"providers[{i}]: must be a JSON object\")\n\n        hosts = entry_raw.get(\"hosts\")\n        if not isinstance(hosts, list) or not hosts:\n            raise ValueError(f\"providers[{i}]: 'hosts' must be a non-empty array\")\n        if not all(isinstance(h, str) and h.strip() for h in hosts):\n            raise ValueError(f\"providers[{i}]: each host must be a non-empty string\")\n        # Normalize hosts: strip whitespace and lowercase\n        hosts = [h.strip().lower() for h in hosts]\n        # Reject dangerous wildcard forms (e.g. *github.com matches github.com.evil.com)\n        for h in hosts:\n            if not _is_valid_host_pattern(h):\n                raise ValueError(\n                    f\"providers[{i}]: invalid host pattern {h!r}. \"\n                    \"Only exact hostnames or '*.suffix' forms are allowed \"\n                    \"(e.g. 'github.com' or '*.visualstudio.com').\"\n                )\n\n        provider = entry_raw.get(\"provider\", \"\")","sourceCodeStart":109,"sourceCodeEnd":145,"githubUrl":"https://github.com/github/spec-kit/blob/bf88c9f9a82fa370c7a7257aa2b3cf10b457b65c/src/specify_cli/authentication/config.py#L109-L145","documentation":"Each element of the auth.json providers array must itself be a JSON object describing one provider. Element i that is a string, number, array, null, or boolean raises ValueError('providers[i]: must be a JSON object'); the index in the message identifies which element is malformed.","triggerScenarios":"\"providers\": [\"github\"] (bare provider name instead of an entry object); an entry that is null from a template placeholder; nested arrays from a copy-paste doubling the brackets: [[{...}]].","commonSituations":"Shortcut configs trying to list provider names as strings; JSONPath/jq transformations that map entries to scalars; removing an entry's contents but leaving a comma, producing null elements.","solutions":["Replace element i (see the index in the error) with an object: {\"hosts\": [...], \"provider\": \"...\", \"auth\": \"...\"}.","Remove null/empty elements entirely rather than leaving placeholders.","Validate with jq: jq '.providers | to_entries | map(select(.value|type!=\"object\"))' auth.json should return []."],"exampleFix":"// before\n{ \"providers\": [\"github\"] }\n// after\n{ \"providers\": [ {\"hosts\": [\"github.com\"], \"provider\": \"github\", \"auth\": \"bearer\"} ] }","handlingStrategy":"validation","validationCode":"raw = json.loads(Path(\"auth.json\").read_text(encoding=\"utf-8\"))\nbad = [i for i, e in enumerate(raw.get(\"providers\", [])) if not isinstance(e, dict)]\nif bad:\n    raise SystemExit(f\"providers entries must be objects; bad indices: {bad}\")","typeGuard":"from typing import Any\n\ndef all_provider_entries_are_objects(providers: Any) -> bool:\n    \"\"\"True when providers is a list whose every element is a dict.\"\"\"\n    return isinstance(providers, list) and all(isinstance(e, dict) for e in providers)","tryCatchPattern":"try:\n    entries = load_auth_config(path)\nexcept ValueError as exc:\n    if \"must be a JSON object\" in str(exc):\n        raise SystemExit(f\"fix provider entry: {exc}\") from exc\n    raise","preventionTips":["Each providers element is an object with hosts/provider/auth fields — never a bare string.","Remove null placeholder entries instead of leaving them.","Use jq '.providers[] | type' to spot non-object entries quickly."],"tags":["authentication","json","configuration","schema"],"backgroundTag":null,"analyzedSha":"bf88c9f9a82fa370c7a7257aa2b3cf10b457b65c","analyzedAt":"2026-08-14T19:43:37.150Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}