HKUDS/Vibe-Trading · error · ValueError
invalid portfolio settings: root must be an object
Error message
invalid portfolio settings: root must be an object
What it means
Raised by SettingsStore.load when the portfolio settings JSON file parses successfully but its top-level value is not a JSON object (e.g. it is an array, string, or number). The loader expects a dict because it feeds the payload straight into parse_settings. This is a file-corruption / wrong-format error, distinct from a JSON syntax error which is raised earlier with the parser's message.
Source
Thrown at agent/src/portfolio/config.py:248
self.connection_store = connection_store or ConnectionStore(connection_path)
def load(self) -> PortfolioSettings:
"""Read persisted settings, creating an empty file on first use.
Returns:
The validated settings currently on disk.
Raises:
ValueError: If the file is unreadable, is not a JSON object, or
fails validation.
"""
if self.path.exists():
try:
payload = json.loads(self.path.read_text(encoding="utf-8"))
except (OSError, json.JSONDecodeError) as exc:
raise ValueError(f"invalid portfolio settings: {exc}") from exc
if not isinstance(payload, dict):
raise ValueError("invalid portfolio settings: root must be an object")
settings = parse_settings(payload, self.connection_store)
if any("profile_id" in source for source in payload.get("sources", [])):
self.save(settings)
return settings
settings = PortfolioSettings()
self.save(settings)
return settings
def save(self, settings: PortfolioSettings | dict[str, Any]) -> PortfolioSettings:
"""Validate and atomically persist settings with owner-only permissions.
Args:
settings: Settings object or raw dict to validate and store.
Returns:
The validated settings that were written.
View on GitHub (pinned to 80ffdda44c)
Solutions
- Inspect the settings file at the store's path and confirm the top level is an object like {"sources": [...]}
- Fix or restore the file (from backup or by deleting it to fall back to defaults, since load constructs PortfolioSettings() when the file is absent)
- If a custom tool writes the file, change it to json.dump({...}, ...) with a dict root
- Re-run settings_store.load() to confirm it parses
Example fix
// before
[{"id": "ibkr", "enabled": true}]
// after
{"sources": [{"id": "ibkr", "enabled": true}]} Defensive patterns
Strategy: validation
Validate before calling
import json
from pathlib import Path
def settings_file_is_valid(path: Path) -> bool:
try:
return isinstance(json.loads(path.read_text(encoding="utf-8")), dict)
except (OSError, json.JSONDecodeError):
return False Type guard
def is_settings_payload(payload: object) -> bool:
return isinstance(payload, dict) Try / catch
try:
settings = store.load()
except ValueError as exc:
if "root must be an object" in str(exc):
# restore backup or delete file to fall back to defaults
path.unlink(missing_ok=True)
settings = store.load()
else:
raise Prevention
- Write settings with json.dump on a dict root only
- Never hand-edit the file into an array form
- Keep a backup of a known-good settings file
- Validate with json.loads + isinstance(dict) after any external tool writes it
When it happens
Trigger: The settings file at self.path contains valid JSON whose root is not an object, e.g. '[]', '"text"', '123', or 'null'. Happens when a user or script overwrites the file with a list of sources or raw serialized fragment instead of an object with keys like 'sources'.
Common situations: Hand-editing settings.json and wrapping everything in brackets; writing a list of sources from a custom script; a truncated or partially-written file from a crashed save; migrating from an older format that stored an array.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- Agent config must decode to a JSON/YAML object
- sources must be a list
- each portfolio source must be an object
- invalid portfolio settings: {exc}
- Feishu QR login requires a JSON agent config; use ~/.vibe-tr
AI-assisted analysis of HKUDS/Vibe-Trading@80ffdda44c (2026-08-28).
Data as JSON: /api/errors/555b19ed20ae9d67.
Report an issue: GitHub.