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
- Validate the file: `python -m json.tool ~/.aicore/config.json` - it prints the exact offset of the syntax error.
- Fix the JSON (remove comments/trailing commas) or, if you no longer use file-based config, delete or rename the file.
- If AICORE_CONFIG points at the wrong file, update or unset it.
- 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
- Run `python -m json.tool` on the config in CI when it changes.
- Write config files atomically (tmp file + rename) so interrupted writes cannot truncate them.
- If you no longer use file-based SAP config, delete the file - absence is fine, corruption is not.
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
- {err.args[0]}
- Failed to retrieve file {file_id} from provider: {str(e)}
- REDIS_CLUSTER_NODES environment variable is not valid JSON.
- 'api_key' is required in litellm_params for WXO agents
- API key is required for OpenAI image variations
AI-assisted analysis of BerriAI/litellm@77b7c6c40c (2026-08-18).
Data as JSON: /api/errors/3a584297b2e84ddb.
Report an issue: GitHub.