BerriAI/litellm · warning · KeyError

{cfg_path} is not valid JSON. Please fix or remove it!

Error message

{cfg_path} is not valid JSON. Please fix or remove it!

What it means

SAP credential bootstrapping reads a config file from $AICORE_CONFIG or $AICORE_HOME/config.json (config_<profile>.json when a profile is set). If the file exists but json.load fails, LiteLLM raises KeyError with the file path, telling you to fix or remove it. Note the unusual exception type: it is a KeyError, not a JSONDecodeError.

Source

Thrown at litellm/llms/sap/credentials.py:148

      1) $AICORE_CONFIG if set, otherwise
      2) $AICORE_HOME/config.json (or config_<profile>.json when profile is given/not default)
    Returns {} when nothing is found.
    """
    home: Final = Path(_get_home())
    profile = profile or os.environ.get(PROFILE_ENV_VAR)
    cfg_env: Final = os.getenv(CONFIG_FILE_ENV_VAR)
    cfg_path: Final = (
        Path(cfg_env)
        if cfg_env
        else (home / ("config.json" if profile in (None, "", "default") else f"config_{profile}.json"))
    )

    if cfg_path and cfg_path.exists():
        try:
            with cfg_path.open(encoding="utf-8") as f:
                return json.load(f)
        except json.JSONDecodeError:
            raise KeyError(f"{cfg_path} is not valid JSON. Please fix or remove it!")

    # If an explicit non-default profile was requested but not found, raise.
    if cfg_env or (profile not in (None, "", "default")):
        raise FileNotFoundError(f"Unable to locate profile config file at '{cfg_path}' in AICORE_HOME '{home}'")

    return {}


def _env_name(name: str) -> str:
    return f"AICORE_{name.upper()}"


def extract_credentials(source: Source) -> dict[str, str]:
    """Extract all credentials from a source."""
    credentials: Final = {}
    for cv in CREDENTIAL_VALUES:
        value = source.get(cv)
        if value is not None:

View on GitHub (pinned to 77b7c6c40c)

Solutions

  1. Validate the file: `python -m json.tool ~/.aicore/config.json` - it prints the exact offset of the syntax error.
  2. Fix the JSON (remove comments/trailing commas) or, if you no longer use file-based config, delete or rename the file.
  3. If AICORE_CONFIG points at the wrong file, update or unset it.
  4. Empty file because nothing was configured? Delete it - an absent file returns {} instead of raising.

Example fix

# before - ~/.aicore/config.json
{ 'AICORE_CLIENT_ID': 'abc', }  # single quotes + trailing comma = invalid JSON
# after
{"AICORE_CLIENT_ID": "abc"}
Defensive patterns

Strategy: validation

Validate before calling

import json, os
from pathlib import Path

def check_sap_config_file() -> Path | None:
    cfg = os.environ.get('AICORE_CONFIG')
    path = Path(cfg) if cfg else Path.home() / '.aicore' / 'config.json'
    if path.exists():
        json.loads(path.read_text(encoding='utf-8'))  # raises JSONDecodeError early with line info
    return path

Try / catch

try:
    litellm.completion(model='sap/...', messages=msgs)
except KeyError as e:
    # note: invalid config JSON surfaces as KeyError, not JSONDecodeError
    if 'is not valid JSON' in str(e):
        fix_config_file()
    raise

Prevention

When it happens

Trigger: A config file exists at the resolved path but contains invalid JSON: trailing commas, comments, single quotes, a BOM, truncation from an interrupted write, or an empty file.

Common situations: Hand-edited ~/.aicore/config.json with a trailing comma or // comment; files written by scripts that truncate on failure; editors saving with UTF-8 BOM; a stale profile file (config_dev.json) left behind after switching auth to env vars.

Related errors


AI-assisted analysis of BerriAI/litellm@77b7c6c40c (2026-08-18). Data as JSON: /api/errors/3a584297b2e84ddb. Report an issue: GitHub.