invoke-ai/InvokeAI · error · RuntimeError
Failed to load api keys file {api_keys_file_path}: expected
Error message
Failed to load api keys file {api_keys_file_path}: expected a mapping What it means
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.
Source
Thrown at invokeai/app/services/config/config_default.py:651
)
return config
except Exception as e:
raise RuntimeError(f"Failed to load config file {config_path}: {e}") from e
def load_external_api_keys(api_keys_file_path: Path) -> dict[str, str]:
"""Load external provider config (API keys and base URLs) from a dedicated YAML file."""
if not api_keys_file_path.exists():
return {}
with open(api_keys_file_path, "rt", encoding=locale.getpreferredencoding()) as file:
loaded_api_keys: Any = yaml.safe_load(file)
if loaded_api_keys is None:
return {}
if not isinstance(loaded_api_keys, dict):
raise RuntimeError(f"Failed to load api keys file {api_keys_file_path}: expected a mapping")
parsed_api_keys: dict[str, str] = {}
for field_name in EXTERNAL_PROVIDER_CONFIG_FIELDS:
value = loaded_api_keys.get(field_name)
if value is None:
continue
if not isinstance(value, str):
raise RuntimeError(
f"Failed to load api keys file {api_keys_file_path}: value for '{field_name}' must be a string"
)
stripped_value = value.strip()
if stripped_value:
parsed_api_keys[field_name] = stripped_value
return parsed_api_keys
@lru_cache(maxsize=1)View on GitHub (pinned to 0b6a024f2f)
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 {})
Example fix
// before (api_keys.yml) - openai: sk-abc - replicate: r8_xxx // after openai: sk-abc replicate: r8_xxx
Defensive patterns
Strategy: validation
Validate before calling
import yaml
from pathlib import Path
def api_keys_file_ok(p: Path) -> bool:
if not p.exists():
return True # missing file is tolerated
data = yaml.safe_load(p.read_text())
return data is None or isinstance(data, dict) Type guard
def is_mapping(v: object) -> bool:
return isinstance(v, dict) Try / catch
try:
keys = load_external_api_keys(path)
except RuntimeError as e:
logger.error(f"bad api keys file: {e}")
keys = {} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Failed to load api keys file {api_keys_file_path}: value for
- Failed to load config file {config_path}: {e}
- str(e)
- Multiuser mode is disabled. Authentication is not required i
- Multiuser mode is disabled. Admin setup is not required in s
AI-assisted analysis of invoke-ai/InvokeAI@0b6a024f2f (2026-08-29).
Data as JSON: /api/errors/3ba7dad134abec51.
Report an issue: GitHub.