{"record":{"id":"d370e567b1b2783e","repo":"odysseus-dev/odysseus","slug":"refusing-to-wipe-non-demo-target-user-host-po","errorCode":null,"errorMessage":"refusing to wipe non-demo target {USER}@{HOST}:{PORT} — set DEMO_ALLOW_WIPE=1 to override","messagePattern":"refusing to wipe non-demo target (.+?)@(.+?):(.+?) — set DEMO_ALLOW_WIPE=1 to override","errorType":"console","errorClass":"SystemExit","httpStatus":null,"severity":"critical","filePath":"scripts/demo_email/seed_demo_emails.py","lineNumber":331,"sourceCode":"def _ensure_mailbox(conn: imaplib.IMAP4, name: str) -> None:\n    if name.upper() == \"INBOX\":\n        return\n    typ, _ = conn.select(name)\n    if typ != \"OK\":\n        conn.create(name)\n\n\ndef _wipe(conn: imaplib.IMAP4) -> int:\n    \"\"\"Delete every message in every mailbox of this (throwaway) account.\n\n    Guard: the connection params are env-overridable, so refuse to run the\n    destructive expunge unless the target is unmistakably the local demo\n    account — otherwise a misconfigured DEMO_IMAP_USER/HOST could irreversibly\n    wipe a real mailbox. Override only with DEMO_ALLOW_WIPE=1 (you must mean it).\n    \"\"\"\n    safe_target = USER.endswith(\"@odysseus.local\") or HOST in (\"localhost\", \"127.0.0.1\", \"::1\")\n    if not safe_target and os.getenv(\"DEMO_ALLOW_WIPE\") != \"1\":\n        raise SystemExit(\n            f\"refusing to wipe non-demo target {USER}@{HOST}:{PORT} — \"\n            f\"set DEMO_ALLOW_WIPE=1 to override\")\n    typ, boxes = conn.list()\n    n = 0\n    names = []\n    if typ == \"OK\":\n        for raw in boxes:\n            line = raw.decode(errors=\"replace\")\n            # last token, possibly quoted, is the mailbox name\n            name = line.split(' \"/\" ')[-1].split(' \".\" ')[-1].strip().strip('\"')\n            names.append(name)\n    for name in set(names) | {\"INBOX\"}:\n        if conn.select(name)[0] != \"OK\":\n            continue\n        typ, data = conn.search(None, \"ALL\")\n        if typ == \"OK\" and data and data[0]:\n            ids = data[0].split()\n            for i in ids:","sourceCodeStart":313,"sourceCodeEnd":349,"githubUrl":"https://github.com/odysseus-dev/odysseus/blob/f9235ebbf13f693a6fd29ce70b097f6ec83705bf/scripts/demo_email/seed_demo_emails.py#L313-L349","documentation":"SystemExit from scripts/demo_email/seed_demo_emails.py's _wipe(): the IMAP connection parameters are env-overridable (DEMO_IMAP_USER/HOST/PORT), and the target is not recognizably the local demo account (user not ending in @odysseus.local and host not localhost/127.0.0.1/::1) while DEMO_ALLOW_WIPE is unset. The script refuses to expunge every message in every mailbox rather than risk wiping a real account.","triggerScenarios":"Running the seed script with DEMO_IMAP_USER=real.user@gmail.com DEMO_IMAP_HOST=imap.gmail.com; pointing at a LAN mail server host like 192.168.1.10 (not in the localhost list); any env override where the safety predicate fails and DEMO_ALLOW_WIPE != '1'.","commonSituations":"Testing the demo flow against a disposable-but-remote mailbox; CI configured with container hostnames (e.g. mail:143) that don't match the safe list; copy-pasting env blocks between environments.","solutions":["Target the actual local demo account (user ending in @odysseus.local, or host localhost/127.0.0.1/::1)","If the remote target is genuinely disposable and you accept the data loss, run with DEMO_ALLOW_WIPE=1","Never point the env vars at a mailbox containing real data"],"exampleFix":"# before\nDEMO_IMAP_USER=real.user@gmail.com DEMO_IMAP_HOST=imap.gmail.com python scripts/demo_email/seed_demo_emails.py\n# SystemExit: refusing to wipe non-demo target ...\n\n# after (throwaway dockerized imap on localhost)\nDEMO_IMAP_USER=demo@odysseus.local DEMO_IMAP_HOST=127.0.0.1 python scripts/demo_email/seed_demo_emails.py","handlingStrategy":"validation","validationCode":"def wipe_target_is_safe(user: str, host: str) -> bool:\n    return user.endswith(\"@odysseus.local\") or host in (\"localhost\", \"127.0.0.1\", \"::1\")\n\nif not wipe_target_is_safe(DEMO_IMAP_USER, DEMO_IMAP_HOST):\n    raise ConfigError(\"refusing wipe of non-demo mailbox; point env at the local demo account\")","typeGuard":null,"tryCatchPattern":"try:\n    run_seed_script(env)\nexcept SystemExit as e:\n    if 'refusing to wipe' in str(e):\n        fix_env_to_local_demo_account()  # never auto-set DEMO_ALLOW_WIPE","preventionTips":["Only ever point DEMO_IMAP_* at the throwaway local demo account","Treat DEMO_ALLOW_WIPE=1 as a destructive, human-approved action — never script it","Put the demo account on localhost so the guard passes naturally"],"tags":["data-safety","demo","imap","guard-rail","script"],"backgroundTag":null,"analyzedSha":"f9235ebbf13f693a6fd29ce70b097f6ec83705bf","analyzedAt":"2026-08-14T21:47:48.359Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}