{"record":{"id":"6a47261e55234b6e","repo":"HKUDS/Vibe-Trading","slug":"agent-config-channels-must-be-an-object","errorCode":null,"errorMessage":"agent config 'channels' must be an object","messagePattern":"agent config 'channels' must be an object","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"agent/src/channels/feishu.py","lineNumber":71,"sourceCode":"    same-directory temporary file and atomically replace the config so readers\n    never observe a partial credential record.\n    \"\"\"\n    from src.config.loader import _read_config_file\n    from src.config.paths import get_config_path\n\n    path = config_path or get_config_path()\n    if path.suffix.lower() != \".json\":\n        raise ValueError(\n            \"Feishu QR login requires a JSON agent config; use \"\n            \"~/.vibe-trading/agent.json\"\n        )\n\n    payload: dict[str, Any] = {}\n    if path.exists():\n        payload = _read_config_file(path)\n    channels = payload.setdefault(\"channels\", {})\n    if not isinstance(channels, dict):\n        raise ValueError(\"agent config 'channels' must be an object\")\n    section = channels.setdefault(\"feishu\", {})\n    if not isinstance(section, dict):\n        raise ValueError(\"agent config 'channels.feishu' must be an object\")\n    section.update(\n        {\n            \"enabled\": True,\n            \"app_id\": app_id,\n            \"app_secret\": app_secret,\n            \"domain\": domain,\n        }\n    )\n\n    path.parent.mkdir(parents=True, exist_ok=True, mode=0o700)\n    content = (json.dumps(payload, ensure_ascii=False, indent=2) + \"\\n\").encode(\"utf-8\")\n    fd, temporary = tempfile.mkstemp(\n        dir=path.parent,\n        prefix=f\".{path.name}.\",\n        suffix=\".tmp\",","sourceCodeStart":53,"sourceCodeEnd":89,"githubUrl":"https://github.com/HKUDS/Vibe-Trading/blob/80ffdda44c5c4db0dd84d70e051cca591cea67df/agent/src/channels/feishu.py#L53-L89","documentation":"When persisting Feishu credentials, the loader expects the top-level 'channels' key in agent.json to be a JSON object. If it exists as an array, string, or number, a ValueError is raised rather than corrupting the config.","triggerScenarios":"agent.json contains \"channels\": [...] or \"channels\": \"feishu\" (any non-object), then QR login writes credentials.","commonSituations":"Hand-edited config mistakes, merging configs from examples that used a different schema, or a previous tool writing channels as a list of names.","solutions":["Edit ~/.vibe-trading/agent.json and make channels an object: \"channels\": {}","Move any channel entries under object keys, e.g. {\"channels\": {\"feishu\": {...}}}","Validate the config with a JSON schema check before rerunning login"],"exampleFix":"// before\n\"channels\": [\"feishu\"]\n// after\n\"channels\": {\"feishu\": {\"enabled\": true}}","handlingStrategy":"type-guard","validationCode":"cfg = json.loads(open(cfg_path).read())\nif 'channels' in cfg and not isinstance(cfg['channels'], dict): raise ValueError('channels must be an object')","typeGuard":"def has_valid_channels(cfg: dict) -> bool:\n    return 'channels' not in cfg or isinstance(cfg['channels'], dict)","tryCatchPattern":null,"preventionTips":["Validate agent.json schema before login","Never store channels as a list"],"tags":["config","feishu","schema","json"],"backgroundTag":"config-schema-invalid","analyzedSha":"80ffdda44c5c4db0dd84d70e051cca591cea67df","analyzedAt":"2026-08-28T12:46:38.989Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}