{"record":{"id":"451d2bb175d84ab1","repo":"langchain-ai/deepagents","slug":"credential-file-path-is-not-a-json-object-delet","errorCode":null,"errorMessage":"Credential file {path} is not a JSON object. Delete it and re-add credentials via /auth.","messagePattern":"Credential file (.+?) is not a JSON object\\. Delete it and re-add credentials via /auth\\.","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"libs/code/deepagents_code/auth_store.py","lineNumber":198,"sourceCode":"    except (UnicodeDecodeError, json.JSONDecodeError) as exc:\n        # `UnicodeDecodeError` (a `ValueError`, not an `OSError`) escapes the\n        # handler above when the file holds non-UTF-8 bytes; treat a decode\n        # failure as corruption so callers get the same `RuntimeError` hint\n        # instead of an unhandled traceback.\n        msg = (\n            f\"Failed to parse credential file {path}: {exc}. \"\n            \"Delete the file and re-add credentials via /auth if it is corrupt.\"\n        )\n        raise RuntimeError(msg) from exc\n    if not isinstance(data, dict):\n        msg = (\n            f\"Credential file {path} is not a JSON object. \"\n            \"Delete it and re-add credentials via /auth.\"\n        )\n        # `RuntimeError` (not `TypeError`) is intentional: every corruption\n        # path here surfaces the same error class so callers can render one\n        # remediation hint regardless of the specific shape problem.\n        raise RuntimeError(msg)  # noqa: TRY004\n    version = data.get(\"version\")\n    if version != _STORAGE_VERSION:\n        msg = (\n            f\"Credential file {path} has unsupported version {version!r} \"\n            f\"(expected {_STORAGE_VERSION}). Delete it and re-add credentials via \"\n            \"/auth.\"\n        )\n        raise RuntimeError(msg)\n    return data\n\n\ndef _write_raw(data: dict) -> tuple[str, ...]:\n    \"\"\"Atomically write `data` as the new auth file with 0600 perms.\n\n    Mirrors `mcp_auth.FileTokenStorage._write` so the security posture is\n    consistent across both stores. If you change this, update\n    `mcp_auth.FileTokenStorage._write` too — they share threat model.\n","sourceCodeStart":180,"sourceCodeEnd":216,"githubUrl":"https://github.com/langchain-ai/deepagents/blob/a1af029e6e73cb17c36bff823d227747b28e91e1/libs/code/deepagents_code/auth_store.py#L180-L216","documentation":"Raised by `_read_raw` in auth_store.py when the credential file parses as valid JSON but is not a JSON object (dict) — e.g. a JSON array, string, number, or null at the top level. The library intentionally raises `RuntimeError` (not `TypeError`) so every corruption path surfaces the same error class and one uniform remediation hint to callers.","triggerScenarios":"Calling load_credentials(), set_stored_key(), or delete_stored_key() when the credential file contains valid JSON whose top level is not an object, such as `[]`, `\"text\"`, `123`, or `null`.","commonSituations":"The file was overwritten by another process writing a JSON array/list of credentials; a user 'cleaned up' the file leaving an empty JSON value; a migration tool wrote an incompatible schema.","solutions":["Delete the credential file at auth_path() and re-add credentials via /auth (set_stored_key).","If the top-level value is a JSON array of credentials, manually convert it to the expected {\"version\": N, \"credentials\": {...}} object shape before retrying.","Back up the file before deleting if any entries are hard to reproduce."],"exampleFix":"// before: file contains [] (not an object)\n// after: reset the store\npathlib.Path(auth_path()).unlink(missing_ok=True)\nset_stored_key(\"anthropic\", key)","handlingStrategy":"type-guard","validationCode":"// pre-check top-level shape before calling the API\ndef _cred_file_shape_ok() -> bool:\n    p = pathlib.Path(auth_path())\n    if not p.exists():\n        return True\n    try:\n        data = json.loads(p.read_text())\n    except (json.JSONDecodeError, OSError):\n        return False\n    return isinstance(data, dict)","typeGuard":"def _is_cred_object(data: object) -> TypeGuard[dict[str, object]]:\n    return isinstance(data, dict)","tryCatchPattern":"try:\n    creds = load_credentials()\nexcept RuntimeError as exc:\n    if \"not a JSON object\" in str(exc):\n        pathlib.Path(auth_path()).unlink(missing_ok=True)\n        creds = {}\n    else:\n        raise","preventionTips":["Do not let other tools write to the credential file path.","Only manipulate credentials through the library API, which always writes an object.","If converting from another format, wrap entries as {\"version\": <current>, \"credentials\": {...}}.","Sanity-check the file with json.tool after any manual migration."],"tags":["auth","json","schema-mismatch","local-storage"],"backgroundTag":"credential-file-corrupt","analyzedSha":"a1af029e6e73cb17c36bff823d227747b28e91e1","analyzedAt":"2026-08-29T11:43:24.718Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}