{"record":{"id":"ce312bb532714ba7","repo":"canopy-network/canopy","slug":"invalid-chain-id-self-chain-id","errorCode":null,"errorMessage":"Invalid chain_id: {self.chain_id}","messagePattern":"Invalid chain_id: (.+?)","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"plugin/python/contract/plugin.py","lineNumber":61,"sourceCode":"# Socket path name (matching Go)\nSOCKET_PATH = \"plugin.sock\"\n\n# PLUGIN_BUILD is a human-readable build marker logged at startup so operators can confirm, via\n# `tail -f /tmp/plugin/python-plugin.log`, that the running binary includes the expected features.\nPLUGIN_BUILD = \"python-plugin v1 (base SDK + detached custom RPC query path)\"\n\n\n@dataclass\nclass Config:\n    \"\"\"Plugin configuration matching Go's Config struct.\"\"\"\n    chain_id: int = 1\n    data_dir_path: str = \"/tmp/plugin/\"\n    # rpc_address is the listen address for the plugin's own HTTP server that exposes custom RPC endpoints\n    rpc_address: str = \"0.0.0.0:50010\"\n\n    def __post_init__(self) -> None:\n        if not isinstance(self.chain_id, int) or self.chain_id < 1:\n            raise ValueError(f\"Invalid chain_id: {self.chain_id}\")\n        if not isinstance(self.data_dir_path, str) or not self.data_dir_path.strip():\n            raise ValueError(f\"Invalid data_dir_path: {self.data_dir_path}\")\n\n\ndef default_config() -> Config:\n    \"\"\"Return the default configuration (matching Go's DefaultConfig).\"\"\"\n    return Config(chain_id=1, data_dir_path=\"/tmp/plugin/\", rpc_address=\"0.0.0.0:50010\")\n\n\ndef new_config_from_file(filepath: str) -> Config:\n    \"\"\"Load configuration from JSON file (matching Go's NewConfigFromFile).\"\"\"\n    try:\n        config_data = json.loads(Path(filepath).read_text(encoding=\"utf-8\"))\n        return Config(\n            chain_id=config_data.get(\"chainId\", 1),\n            data_dir_path=config_data.get(\"dataDirPath\", \"/tmp/plugin/\"),\n            rpc_address=config_data.get(\"rpcAddress\", \"0.0.0.0:50010\"),\n        )","sourceCodeStart":43,"sourceCodeEnd":79,"githubUrl":"https://github.com/canopy-network/canopy/blob/ee8197d91dd410f6592cb650a94c925ee6dc8bad/plugin/python/contract/plugin.py#L43-L79","documentation":"The plugin Config dataclass validates itself in __post_init__: chain_id must be an int >= 1 and data_dir_path a non-empty string. If chain_id is not an int or is less than 1, Config construction raises ValueError(f\"Invalid chain_id: {self.chain_id}\"), preventing the plugin from starting with an invalid chain identity.","triggerScenarios":"Constructing Config(chain_id=0), a negative value, a string like '1' from parsed JSON config, or None when chain.json's ChainId field is missing or of the wrong type.","commonSituations":"chain.json missing the ChainId field so it defaults/parse to None; a config loader passing strings without conversion; hand-editing chain.json and setting ChainId to 0 or a quoted value.","solutions":["Set ChainId to a positive integer (>= 1) in chain.json / the Config constructor, and restart the plugin.","If your config layer yields strings, convert with int(raw) before constructing Config, and handle non-numeric input explicitly.","Check the config-loading code for a missing-field default that injects None/0, and make ChainId required at parse time.","Add a pre-flight check of parsed config values (isinstance int, >= 1) with a clear message before starting the plugin."],"exampleFix":"// before\nConfig(chain_id=str(cfg['ChainId']))  # ValueError\n// after\nConfig(chain_id=int(cfg['ChainId']))  # e.g. 1","handlingStrategy":"validation","validationCode":"raw = cfg.get('ChainId')\nchain_id = int(raw) if isinstance(raw, str) and raw.isdigit() else raw\nif not isinstance(chain_id, int) or chain_id < 1:\n    raise ValueError(f'ChainId must be a positive integer, got: {raw!r}')\nconfig = Config(chain_id=chain_id)","typeGuard":"def is_valid_chain_id(v) -> bool:\n    return isinstance(v, int) and not isinstance(v, bool) and v >= 1","tryCatchPattern":"try:\n    config = Config(chain_id=cfg['ChainId'], data_dir_path=cfg['DataDirPath'])\nexcept ValueError as e:\n    logger.error('invalid plugin config: %s', e)\n    sys.exit(1)","preventionTips":["Always set ChainId as a positive integer in chain.json.","Convert string config values to int at parse time.","Validate the whole parsed config before constructing Config.","Cover Config validation with unit tests for 0, negative, string, and None values."],"tags":["python","configuration","validation","dataclass"],"backgroundTag":"invalid-config-value","analyzedSha":"ee8197d91dd410f6592cb650a94c925ee6dc8bad","analyzedAt":"2026-09-06T09:30:15.973Z","contentChangedAt":"2026-09-06T09:30:15.973Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}