HKUDS/Vibe-Trading · critical · ValueError

invalid portfolio settings: {exc}

Error message

invalid portfolio settings: {exc}

What it means

PortfolioSettingsStore.load wraps any OSError or JSONDecodeError raised while reading the settings file into a single ValueError with the underlying cause chained. It means the file exists but cannot be read or parsed, distinct from the downstream parse_settings schema errors.

Source

Thrown at agent/src/portfolio/config.py:246

            self.path.with_name("connections.json") if path is not None else None
        )
        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:

View on GitHub (pinned to 80ffdda44c)

Solutions

  1. Read the chained cause (__cause__) to see whether it is OSError (fix permissions/disk) or JSONDecodeError (fix syntax)
  2. Repair the JSON with json.tool or restore from backup
  3. Ensure only the library writes the file and the process has consistent permissions

Example fix

try:
    settings = store.load()
except ValueError as exc:
    cause = exc.__cause__  # OSError vs json.JSONDecodeError
    log.error("portfolio settings unreadable: %s (%s)", exc, cause)
Defensive patterns

Strategy: try-catch

Validate before calling

import json
try:
    json.loads(store.path.read_text(encoding='utf-8'))
except (OSError, json.JSONDecodeError) as exc:
    alert(f'portfolio settings unreadable: {exc}')

Type guard

def settings_loadable(path) -> bool:
    try:
        return isinstance(json.loads(path.read_text(encoding='utf-8')), dict)
    except (OSError, json.JSONDecodeError):
        return False

Try / catch

try:
    settings = store.load()
except ValueError as exc:
    cause = exc.__cause__
    if isinstance(cause, OSError):
        fix_permissions(store.path); settings = store.load()
    elif isinstance(cause, json.JSONDecodeError):
        settings = restore_settings_backup(store)

Prevention

When it happens

Trigger: An unreadable file (permissions, I/O error) or malformed JSON at the configured settings path; any consumer (settings, sources, refresh, latest, reconnect_target, _fetch_fx) then propagates this error.

Common situations: Partial writes from a crashed process, permissions changed after a user switch, or manual edits introducing syntax errors in the settings file.

Related errors


AI-assisted analysis of HKUDS/Vibe-Trading@80ffdda44c (2026-08-28). Data as JSON: /api/errors/c7e75f3a8f4ac14e. Report an issue: GitHub.