{"record":{"id":"3a584297b2e84ddb","repo":"BerriAI/litellm","slug":"cfg-path-is-not-valid-json-please-fix-or-remove","errorCode":null,"errorMessage":"{cfg_path} is not valid JSON. Please fix or remove it!","messagePattern":"(.+?) is not valid JSON\\. Please fix or remove it!","errorType":"exception","errorClass":"KeyError","httpStatus":null,"severity":"warning","filePath":"litellm/llms/sap/credentials.py","lineNumber":148,"sourceCode":"      1) $AICORE_CONFIG if set, otherwise\n      2) $AICORE_HOME/config.json (or config_<profile>.json when profile is given/not default)\n    Returns {} when nothing is found.\n    \"\"\"\n    home: Final = Path(_get_home())\n    profile = profile or os.environ.get(PROFILE_ENV_VAR)\n    cfg_env: Final = os.getenv(CONFIG_FILE_ENV_VAR)\n    cfg_path: Final = (\n        Path(cfg_env)\n        if cfg_env\n        else (home / (\"config.json\" if profile in (None, \"\", \"default\") else f\"config_{profile}.json\"))\n    )\n\n    if cfg_path and cfg_path.exists():\n        try:\n            with cfg_path.open(encoding=\"utf-8\") as f:\n                return json.load(f)\n        except json.JSONDecodeError:\n            raise KeyError(f\"{cfg_path} is not valid JSON. Please fix or remove it!\")\n\n    # If an explicit non-default profile was requested but not found, raise.\n    if cfg_env or (profile not in (None, \"\", \"default\")):\n        raise FileNotFoundError(f\"Unable to locate profile config file at '{cfg_path}' in AICORE_HOME '{home}'\")\n\n    return {}\n\n\ndef _env_name(name: str) -> str:\n    return f\"AICORE_{name.upper()}\"\n\n\ndef extract_credentials(source: Source) -> dict[str, str]:\n    \"\"\"Extract all credentials from a source.\"\"\"\n    credentials: Final = {}\n    for cv in CREDENTIAL_VALUES:\n        value = source.get(cv)\n        if value is not None:","sourceCodeStart":130,"sourceCodeEnd":166,"githubUrl":"https://github.com/BerriAI/litellm/blob/77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8/litellm/llms/sap/credentials.py#L130-L166","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"# before - ~/.aicore/config.json\n{ 'AICORE_CLIENT_ID': 'abc', }  # single quotes + trailing comma = invalid JSON\n# after\n{\"AICORE_CLIENT_ID\": \"abc\"}","handlingStrategy":"validation","validationCode":"import json, os\nfrom pathlib import Path\n\ndef check_sap_config_file() -> Path | None:\n    cfg = os.environ.get('AICORE_CONFIG')\n    path = Path(cfg) if cfg else Path.home() / '.aicore' / 'config.json'\n    if path.exists():\n        json.loads(path.read_text(encoding='utf-8'))  # raises JSONDecodeError early with line info\n    return path","typeGuard":null,"tryCatchPattern":"try:\n    litellm.completion(model='sap/...', messages=msgs)\nexcept KeyError as e:\n    # note: invalid config JSON surfaces as KeyError, not JSONDecodeError\n    if 'is not valid JSON' in str(e):\n        fix_config_file()\n    raise","preventionTips":["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."],"tags":["sap","credentials","config-file","json"],"backgroundTag":"invalid-config-file","analyzedSha":"77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8","analyzedAt":"2026-08-18T11:44:31.656Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}