BigPizzaV3/CodexPlusPlus · error · ValueError

codex config must be an object

Error message

codex config must be an object

What it means

load_config reads the config.toml-shaped JSON file and requires the top-level `codex` key to be a JSON object. It fires when the user's config file has a `codex` entry that is a string, number, array, or null (explicit `{}` is fine), meaning the bridge cannot extract nested fields like codex.command/args/timeouts from it.

Solutions

  1. Open the config file and change the `codex` value to a JSON object, e.g. "codex": {"command": "codex", "args": ["app-server"]}.
  2. If `codex` is not needed, remove the key entirely — the code falls back to raw.get("codex") or {}.
  3. Validate the file with a JSON linter to ensure no quoting mistakes turned the object into a string.
Defensive patterns

Strategy: validation

When it happens

Trigger: Thrown at tools/codex-wechat/codex_wechat.py:119 when the library encounters an invalid state.

Common situations: See trigger scenarios.


AI-assisted analysis of BigPizzaV3/CodexPlusPlus@b1ed92e5e4 (2026-09-19). Data as JSON: /api/errors/fb87ac13e560bee7. Report an issue: GitHub.

Appendix: source

Thrown at tools/codex-wechat/codex_wechat.py:119

            with LOG_FILE.open("a", encoding="utf-8") as f:
                f.write(line + "\n")
        except Exception:
            pass


def load_json_file(path: Path) -> dict[str, Any]:
    with path.open("r", encoding="utf-8") as f:
        data = json.load(f)
    if not isinstance(data, dict):
        raise ValueError(f"{path} must contain a JSON object")
    return data


def load_config(path: Path) -> BridgeConfig:
    raw = load_json_file(path)
    codex_raw = raw.get("codex") or {}
    if not isinstance(codex_raw, dict):
        raise ValueError("codex config must be an object")

    cfg = BridgeConfig(
        base_url=str(raw.get("base_url") or ILINK_BASE_URL),
        token=str(raw.get("token") or ""),
        account_id=str(raw.get("account_id") or ""),
        allow_all=bool(raw.get("allow_all", False)),
        allow_user_ids=set(str(x) for x in raw.get("allow_user_ids") or []),
        require_prefix=bool(raw.get("require_prefix", True)),
        prefix=str(raw.get("prefix") or "/codex"),
        allow_id_command_for_unauthorized=bool(
            raw.get("allow_id_command_for_unauthorized", True)
        ),
        poll_timeout_seconds=int(raw.get("poll_timeout_seconds", 35)),
        retry_delay_seconds=int(raw.get("retry_delay_seconds", 2)),
        backoff_delay_seconds=int(raw.get("backoff_delay_seconds", 30)),
        max_consecutive_failures=int(raw.get("max_consecutive_failures", 3)),
        max_reply_chars=int(raw.get("max_reply_chars", 1800)),
        enable_typing=bool(raw.get("enable_typing", True)),

View on GitHub (pinned to b1ed92e5e4)