{"record":{"id":"e33501d1e819658d","repo":"ruvnet/RuView","slug":"access-denied-to-zone-zone-id","errorCode":null,"errorMessage":"Access denied to zone '{zone_id}'","messagePattern":"Access denied to zone '(.+?)'","errorType":"http","errorClass":"HTTPException","httpStatus":403,"severity":"error","filePath":"archive/v1/src/api/dependencies.py","lineNumber":204,"sourceCode":"        )\n    \n    # Check if zone is enabled\n    if not zone.enabled:\n        raise HTTPException(\n            status_code=status.HTTP_403_FORBIDDEN,\n            detail=f\"Zone '{zone_id}' is disabled\"\n        )\n    \n    # If authentication is enabled, check user access\n    if current_user:\n        # Admin users have access to all zones\n        if current_user.get(\"is_admin\", False):\n            return zone_id\n        \n        # Check user's zone permissions\n        user_zones = current_user.get(\"zones\", [])\n        if user_zones and zone_id not in user_zones:\n            raise HTTPException(\n                status_code=status.HTTP_403_FORBIDDEN,\n                detail=f\"Access denied to zone '{zone_id}'\"\n            )\n    \n    return zone_id\n\n\n# Router access dependencies\nasync def validate_router_access(\n    router_id: str,\n    current_user: Optional[Dict[str, Any]] = Depends(get_current_user)\n) -> str:\n    \"\"\"Validate user access to a specific router.\"\"\"\n    domain_config = get_domain_config()\n    \n    # Check if router exists\n    router = domain_config.get_router(router_id)\n    if not router:","sourceCodeStart":186,"sourceCodeEnd":222,"githubUrl":"https://github.com/ruvnet/RuView/blob/4685618388a5e49fad5b3005806f3bdd6a7c25c3/archive/v1/src/api/dependencies.py#L186-L222","documentation":"validate_zone_access raises 403 \"Access denied to zone '<zone_id>'\" when the authenticated user is not an admin, their zones list is non-empty, and the requested zone is not in it. Note the semantics: an empty zones list means 'no restriction' — denial only happens when a list exists and the zone is absent from it.","triggerScenarios":"A user provisioned with zones=['beta'] calling a route for zone 'alpha'; scoped service accounts missing the newly added zone; stale tokens whose zones claim predates a zone addition.","commonSituations":"Onboarding a user without adding the new zone to their allowlist; per-zone tenancy enforcement; forgotten update of the user's zones after zone creation.","solutions":["Add the zone to the user's zones allowlist (or request access from an admin)","Use an admin token, which bypasses the zone allowlist","If the user should see all zones, clear their zones list (empty means unrestricted)"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"# Check the token's zone allowlist before the call (empty list = unrestricted)\nclaims = jwt.decode(token, options={'verify_signature': False})\nzones = claims.get('zones') or []\nif zones and zone_id not in zones and not claims.get('is_admin'):\n    raise PermissionError(f'Token not scoped for zone {zone_id}')\nclient.get(f'/api/zones/{zone_id}/pose', headers=auth)","typeGuard":null,"tryCatchPattern":"try:\n    r = client.get(f'/api/zones/{zone_id}/pose', headers=auth)\n    r.raise_for_status()\nexcept httpx.HTTPStatusError as e:\n    if e.response.status_code == 403 and 'Access denied' in e.response.text:\n        # scope problem: request access or switch to an allowed zone\n        switch_to_allowed_zone()\n    raise","preventionTips":["Update user zone allowlists whenever new zones are provisioned","Decode and inspect the zones claim when troubleshooting 403s","Remember empty zones list means unrestricted — explicit allowlists deny everything else"],"tags":["authorization","zones","http-403","python"],"backgroundTag":null,"analyzedSha":"4685618388a5e49fad5b3005806f3bdd6a7c25c3","analyzedAt":"2026-08-16T06:09:40.886Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}