{"record":{"id":"555b19ed20ae9d67","repo":"HKUDS/Vibe-Trading","slug":"invalid-portfolio-settings-root-must-be-an-object","errorCode":null,"errorMessage":"invalid portfolio settings: root must be an object","messagePattern":"invalid portfolio settings: root must be an object","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"agent/src/portfolio/config.py","lineNumber":248,"sourceCode":"        self.connection_store = connection_store or ConnectionStore(connection_path)\n\n    def load(self) -> PortfolioSettings:\n        \"\"\"Read persisted settings, creating an empty file on first use.\n\n        Returns:\n            The validated settings currently on disk.\n\n        Raises:\n            ValueError: If the file is unreadable, is not a JSON object, or\n                fails validation.\n        \"\"\"\n        if self.path.exists():\n            try:\n                payload = json.loads(self.path.read_text(encoding=\"utf-8\"))\n            except (OSError, json.JSONDecodeError) as exc:\n                raise ValueError(f\"invalid portfolio settings: {exc}\") from exc\n            if not isinstance(payload, dict):\n                raise ValueError(\"invalid portfolio settings: root must be an object\")\n            settings = parse_settings(payload, self.connection_store)\n            if any(\"profile_id\" in source for source in payload.get(\"sources\", [])):\n                self.save(settings)\n            return settings\n\n        settings = PortfolioSettings()\n        self.save(settings)\n        return settings\n\n    def save(self, settings: PortfolioSettings | dict[str, Any]) -> PortfolioSettings:\n        \"\"\"Validate and atomically persist settings with owner-only permissions.\n\n        Args:\n            settings: Settings object or raw dict to validate and store.\n\n        Returns:\n            The validated settings that were written.\n","sourceCodeStart":230,"sourceCodeEnd":266,"githubUrl":"https://github.com/HKUDS/Vibe-Trading/blob/80ffdda44c5c4db0dd84d70e051cca591cea67df/agent/src/portfolio/config.py#L230-L266","documentation":"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.","triggerScenarios":"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'.","commonSituations":"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.","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"],"exampleFix":"// before\n[{\"id\": \"ibkr\", \"enabled\": true}]\n// after\n{\"sources\": [{\"id\": \"ibkr\", \"enabled\": true}]}","handlingStrategy":"validation","validationCode":"import json\nfrom pathlib import Path\n\ndef settings_file_is_valid(path: Path) -> bool:\n    try:\n        return isinstance(json.loads(path.read_text(encoding=\"utf-8\")), dict)\n    except (OSError, json.JSONDecodeError):\n        return False","typeGuard":"def is_settings_payload(payload: object) -> bool:\n    return isinstance(payload, dict)","tryCatchPattern":"try:\n    settings = store.load()\nexcept ValueError as exc:\n    if \"root must be an object\" in str(exc):\n        # restore backup or delete file to fall back to defaults\n        path.unlink(missing_ok=True)\n        settings = store.load()\n    else:\n        raise","preventionTips":["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"],"tags":["python","json","config","portfolio","schema-validation"],"backgroundTag":"schema-validation-failed","analyzedSha":"80ffdda44c5c4db0dd84d70e051cca591cea67df","analyzedAt":"2026-08-28T12:46:38.989Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}