{"record":{"id":"3ccc0c66334d65e4","repo":"can1357/oh-my-pi","slug":"robomp-bot-login-must-be-a-non-empty-github-login","errorCode":null,"errorMessage":"ROBOMP_BOT_LOGIN must be a non-empty GitHub login","messagePattern":"ROBOMP_BOT_LOGIN must be a non-empty GitHub login","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"python/robomp/src/config.py","lineNumber":180,"sourceCode":"    natives_cache_gc_interval_seconds: float = Field(3600.0, alias=\"ROBOMP_NATIVES_CACHE_GC_INTERVAL_SECONDS\")\n\n    # Post-run workspace cache reclamation. Every task run reinstalls\n    # node_modules (`ensure_workspace_dependencies`), so between runs the\n    # checkout's node_modules and the workspace-private bun install cache are\n    # dead weight — multiple GB per issue that would otherwise persist until\n    # the issue closes and exhaust the disk. When enabled, the worker strips\n    # them after every event and WorkerPool.start() sweeps all workspaces once\n    # at boot. Costs a dependency re-download on the next run for that issue.\n    reclaim_workspace_caches: bool = Field(True, alias=\"ROBOMP_RECLAIM_WORKSPACE_CACHES\")\n\n    @field_validator(\"bot_login\", mode=\"after\")\n    @classmethod\n    def _require_bot_login(cls, value: str) -> str:\n        cleaned = value.strip().removeprefix(\"@\").lower()\n        if cleaned.endswith(\"[bot]\"):\n            cleaned = cleaned[:-5]\n        if not cleaned:\n            raise ValueError(\"ROBOMP_BOT_LOGIN must be a non-empty GitHub login\")\n        return cleaned\n\n    @field_validator(\"replay_token\", mode=\"before\")\n    @classmethod\n    def _blank_replay_disables(cls, value: object) -> object:\n        # Treat empty/whitespace strings as 'disabled'. Without this, an empty\n        # ROBOMP_REPLAY_TOKEN becomes SecretStr(\"\") which the server would\n        # happily compare against an empty X-Robomp-Replay-Token header.\n        if isinstance(value, str) and not value.strip():\n            return None\n        if hasattr(value, \"get_secret_value\"):\n            inner = value.get_secret_value()  # type: ignore[attr-defined]\n            if isinstance(inner, str) and not inner.strip():\n                return None\n        return value\n\n    @field_validator(\"github_token\", mode=\"before\")\n    @classmethod","sourceCodeStart":162,"sourceCodeEnd":198,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/python/robomp/src/config.py#L162-L198","documentation":"The Settings model normalizes ROBOMP_BOT_LOGIN with a pydantic validator that strips whitespace and a leading @, removes a trailing \"[bot]\" suffix, and requires a non-empty remainder. An empty value (or one that normalizes to empty, like \"@[bot]\") raises ValueError during config validation.","triggerScenarios":"Setting ROBOMP_BOT_LOGIN to \"\", whitespace, \"@\", \"[bot]\", or \"@[bot]\" when constructing Settings (e.g. from env at CLI startup).","commonSituations":"Empty placeholder in .env files (ROBOMP_BOT_LOGIN=); template variable that failed to interpolate; user pasting just the [bot] suffix expecting the validator to infer a name; CI secrets configured as empty strings.","solutions":["Set ROBOMP_BOT_LOGIN to an actual GitHub login, e.g. ROBOMP_BOT_LOGIN=my-robot.","You may include @ and [bot] suffix — they are stripped — but the underlying name must be non-empty.","If the bot is optional, remove the variable entirely if the schema allows omission rather than leaving it blank.","Check .env interpolation so an empty CI variable is not silently written."],"exampleFix":"// before (.env)\nROBOMP_BOT_LOGIN=@[bot]\n// after\nROBOMP_BOT_LOGIN=@my-robot[bot]","handlingStrategy":"validation","validationCode":"login = (os.environ.get(\"ROBOMP_BOT_LOGIN\") or \"\").strip().removeprefix(\"@\").removesuffix(\"[bot]\")\nif not login:\n    raise SystemExit(\"ROBOMP_BOT_LOGIN must name a bot account, e.g. 'my-robot' or '@my-robot[bot]'\")","typeGuard":"def is_valid_bot_login(value: str) -> bool:\n    cleaned = value.strip().removeprefix(\"@\").removesuffix(\"[bot]\")\n    return bool(cleaned)","tryCatchPattern":"try:\n    cfg = Settings(_env_file=\".env\")\nexcept ValidationError as exc:\n    sys.exit(f\"configuration error: {exc}\")","preventionTips":["Never leave ROBOMP_BOT_LOGIN as an empty placeholder in .env files.","Check CI/template interpolation so empty variables are not written.","Remember the validator strips @ and [bot] — supply the underlying account name."],"tags":["python","configuration","validation","pydantic","env-var"],"backgroundTag":"invalid-environment-configuration","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}