{"record":{"id":"fb2a1b90bbfd1182","repo":"can1357/oh-my-pi","slug":"robomp-gh-proxy-url-and-robomp-gh-proxy-hmac-key-m","errorCode":null,"errorMessage":"ROBOMP_GH_PROXY_URL and ROBOMP_GH_PROXY_HMAC_KEY must both be set together (or both empty).","messagePattern":"ROBOMP_GH_PROXY_URL and ROBOMP_GH_PROXY_HMAC_KEY must both be set together \\(or both empty\\)\\.","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"python/robomp/src/config.py","lineNumber":247,"sourceCode":"    def _validate_proxy_or_pat(self) -> Settings:\n        \"\"\"Enforce mutual exclusion between PAT and proxy mode.\n\n        - Both set → reject (silent fallback to direct GitHub would defeat\n          the isolation goal).\n        - Proxy URL set but no HMAC key (or vice versa) → reject (gh-proxy\n          would either be unauthenticated or unreachable).\n        - Neither set → also reject; SOMETHING needs to talk to GitHub.\n        \"\"\"\n        has_token = self.github_token is not None\n        has_url = bool(self.gh_proxy_url)\n        has_key = self.gh_proxy_hmac_key is not None\n        if has_token and has_url:\n            raise ValueError(\n                \"GITHUB_TOKEN and ROBOMP_GH_PROXY_URL are mutually exclusive — \"\n                \"set ONE to choose between direct-PAT and gh-proxy modes.\"\n            )\n        if has_url != has_key:\n            raise ValueError(\n                \"ROBOMP_GH_PROXY_URL and ROBOMP_GH_PROXY_HMAC_KEY must both be set together (or both empty).\"\n            )\n        if not has_token and not has_url:\n            raise ValueError(\n                \"no GitHub access configured: set GITHUB_TOKEN, or set \"\n                \"ROBOMP_GH_PROXY_URL + ROBOMP_GH_PROXY_HMAC_KEY to use gh-proxy.\"\n            )\n        return self\n\n    @field_validator(\"repo_allowlist_raw\", mode=\"before\")\n    @classmethod\n    def _coerce_allowlist(cls, v: object) -> str:\n        if v is None:\n            return \"\"\n        if isinstance(v, str):\n            return v\n        if isinstance(v, (list, tuple)):\n            return \",\".join(str(item) for item in v)","sourceCodeStart":229,"sourceCodeEnd":265,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/python/robomp/src/config.py#L229-L265","documentation":"The _validate_proxy_or_pat() model validator requires ROBOMP_GH_PROXY_URL and ROBOMP_GH_PROXY_HMAC_KEY to be set together: proxy mode without the HMAC key cannot sign requests, and an HMAC key without a URL has nowhere to send them. A mismatch (has_url != has_key) raises ValueError.","triggerScenarios":"Constructing Settings with gh_proxy_url set but gh_proxy_hmac_key missing, or the key set while the URL is empty/absent.","commonSituations":"Only the URL added to .env (the key was meant to come from a secret store but was not wired); key rotation removed one variable; typo in one of the two names; partial copy-paste of a config snippet.","solutions":["Set both ROBOMP_GH_PROXY_URL and ROBOMP_GH_PROXY_HMAC_KEY together.","If you don't intend to use gh-proxy, remove both variables and configure GITHUB_TOKEN instead.","Check secret injection so the HMAC key is actually mounted/interpolated.","Verify exact env var spelling in .env and deployment manifests."],"exampleFix":"// before\nROBOMP_GH_PROXY_URL=http://gh-proxy:8080\n# ROBOMP_GH_PROXY_HMAC_KEY missing\n// after\nROBOMP_GH_PROXY_URL=http://gh-proxy:8080\nROBOMP_GH_PROXY_HMAC_KEY=<shared-secret>","handlingStrategy":"validation","validationCode":"import os\nhas_url, has_key = bool(os.environ.get(\"ROBOMP_GH_PROXY_URL\")), bool(os.environ.get(\"ROBOMP_GH_PROXY_HMAC_KEY\"))\nif has_url != has_key:\n    raise SystemExit(\"ROBOMP_GH_PROXY_URL and ROBOMP_GH_PROXY_HMAC_KEY must be set together\")","typeGuard":"def proxy_pair_complete(env: dict) -> bool:\n    return bool(env.get(\"ROBOMP_GH_PROXY_URL\")) == bool(env.get(\"ROBOMP_GH_PROXY_HMAC_KEY\"))","tryCatchPattern":"try:\n    cfg = Settings(_env_file=\".env\")\nexcept ValidationError as exc:\n    sys.exit(f\"configuration error: {exc}\")","preventionTips":["Store the URL and HMAC key adjacent in the same config file/secret set so they ship together.","Wire secret-store values into env explicitly — don't assume defaults.","Watch for key rotation scripts that update one variable but not the other."],"tags":["python","configuration","validation","pydantic","github"],"backgroundTag":"invalid-environment-configuration","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}