{"record":{"id":"3ba7dad134abec51","repo":"invoke-ai/InvokeAI","slug":"failed-to-load-api-keys-file-api-keys-file-path","errorCode":null,"errorMessage":"Failed to load api keys file {api_keys_file_path}: expected a mapping","messagePattern":"Failed to load api keys file (.+?): expected a mapping","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"invokeai/app/services/config/config_default.py","lineNumber":651,"sourceCode":"        )\n        return config\n    except Exception as e:\n        raise RuntimeError(f\"Failed to load config file {config_path}: {e}\") from e\n\n\ndef load_external_api_keys(api_keys_file_path: Path) -> dict[str, str]:\n    \"\"\"Load external provider config (API keys and base URLs) from a dedicated YAML file.\"\"\"\n    if not api_keys_file_path.exists():\n        return {}\n\n    with open(api_keys_file_path, \"rt\", encoding=locale.getpreferredencoding()) as file:\n        loaded_api_keys: Any = yaml.safe_load(file)\n\n    if loaded_api_keys is None:\n        return {}\n\n    if not isinstance(loaded_api_keys, dict):\n        raise RuntimeError(f\"Failed to load api keys file {api_keys_file_path}: expected a mapping\")\n\n    parsed_api_keys: dict[str, str] = {}\n    for field_name in EXTERNAL_PROVIDER_CONFIG_FIELDS:\n        value = loaded_api_keys.get(field_name)\n        if value is None:\n            continue\n        if not isinstance(value, str):\n            raise RuntimeError(\n                f\"Failed to load api keys file {api_keys_file_path}: value for '{field_name}' must be a string\"\n            )\n        stripped_value = value.strip()\n        if stripped_value:\n            parsed_api_keys[field_name] = stripped_value\n\n    return parsed_api_keys\n\n\n@lru_cache(maxsize=1)","sourceCodeStart":633,"sourceCodeEnd":669,"githubUrl":"https://github.com/invoke-ai/InvokeAI/blob/0b6a024f2ff6a86bfb953dcdb9cc504ef7397a06/invokeai/app/services/config/config_default.py#L633-L669","documentation":"load_external_api_keys parses the dedicated external provider API keys YAML file and requires its top level to be a mapping (dict). If yaml.safe_load returns a non-dict value (list, scalar), it raises this RuntimeError because provider fields cannot be looked up by name.","triggerScenarios":"Calling load_external_api_keys (via get_config or _apply_external_provider_update) when api_keys_file_path exists but its YAML root is a list, string, number, or boolean.","commonSituations":"api_keys.yml contains only a bare key like `sk-abc123` (no top-level mapping), or a list of key/value pairs in the wrong shape, or the file was written by a script using an unexpected format.","solutions":["Restructure the file so the top level is a mapping of EXTERNAL_PROVIDER_CONFIG_FIELDS names to string values","Ensure each entry is `field_name: value` with the value on the same line, not a nested list","Quote values that YAML could misinterpret as other types (e.g. keys starting with numbers)","If the file is junk and not needed, delete it (a missing file is tolerated and returns {})"],"exampleFix":"// before (api_keys.yml)\n- openai: sk-abc\n- replicate: r8_xxx\n// after\n\nopenai: sk-abc\nreplicate: r8_xxx","handlingStrategy":"validation","validationCode":"import yaml\nfrom pathlib import Path\ndef api_keys_file_ok(p: Path) -> bool:\n    if not p.exists():\n        return True  # missing file is tolerated\n    data = yaml.safe_load(p.read_text())\n    return data is None or isinstance(data, dict)","typeGuard":"def is_mapping(v: object) -> bool:\n    return isinstance(v, dict)","tryCatchPattern":"try:\n    keys = load_external_api_keys(path)\nexcept RuntimeError as e:\n    logger.error(f\"bad api keys file: {e}\")\n    keys = {}","preventionTips":["Always write API keys files as top-level `field: value` mappings","Never store bare secrets without a key name","Validate the file with yaml.safe_load + isinstance(dict) after edits","Use one canonical filename and template from docs"],"tags":["config","yaml","api-keys"],"backgroundTag":"config-file-invalid","analyzedSha":"0b6a024f2ff6a86bfb953dcdb9cc504ef7397a06","analyzedAt":"2026-08-29T04:46:49.967Z","schemaVersion":2},"datasetVersion":"2026-08-29T07:17:48.351Z"}