{"record":{"id":"47a82acf9897e73e","repo":"BerriAI/litellm","slug":"user-valid-token-user-id-has-been-deactivated-vi","errorCode":null,"errorMessage":"User={valid_token.user_id} has been deactivated via SCIM. Keys owned by this user cannot be used.","messagePattern":"User=(.+?) has been deactivated via SCIM\\. Keys owned by this user cannot be used\\.","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"litellm/proxy/auth/user_api_key_auth.py","lineNumber":1819,"sourceCode":"                            prisma_client=prisma_client,\n                            user_api_key_cache=user_api_key_cache,\n                            user_id_upsert=False,\n                            parent_otel_span=parent_otel_span,\n                            proxy_logging_obj=proxy_logging_obj,\n                        )\n                except Exception as e:\n                    verbose_logger.debug(\n                        \"litellm.proxy.auth.user_api_key_auth.py::user_api_key_auth() - Unable to get user from db/cache. Setting user_obj to None. Exception received - %s\",\n                        e,\n                    )\n                    user_obj = None\n\n                if (\n                    user_obj is not None\n                    and isinstance(user_obj.metadata, dict)\n                    and user_obj.metadata.get(\"scim_active\") is False\n                ):\n                    raise Exception(\n                        f\"User={valid_token.user_id} has been deactivated via SCIM. Keys owned by this user cannot be used.\"\n                    )\n\n            # Check 2a. Check if model has zero cost - if so, skip all budget checks\n            model = _get_model_from_request_context(\n                request_data=request_data,\n                route=route,\n                request=request,\n                llm_router=llm_router,\n            )\n            skip_budget_checks = False\n            if model is not None and llm_router is not None:\n                from litellm.proxy.auth.auth_checks import _is_model_cost_zero\n\n                skip_budget_checks = _is_model_cost_zero(model=model, llm_router=llm_router)\n                if skip_budget_checks:\n                    verbose_proxy_logger.info(\"Skipping all budget checks for zero-cost model: %s\", model)\n","sourceCodeStart":1801,"sourceCodeEnd":1837,"githubUrl":"https://github.com/BerriAI/litellm/blob/77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8/litellm/proxy/auth/user_api_key_auth.py#L1801-L1837","documentation":"SCIM offboarding enforcement: the token's owning user was found (DB or cache) and their `metadata.scim_active` is `False`, meaning the IdP deactivated the user via SCIM. LiteLLM refuses all keys owned by a SCIM-deactivated user even if the key itself is valid, unblocked, and unexpired.","triggerScenarios":"IdP (Okta/Entra) deprovisions a user, SCIM sync sets `metadata.scim_active=false`; any of that user's keys are then used for any request — check 2's user lookup reads `scim_active` and raises. Re-activation requires SCIM setting it back or an admin clearing the flag.","commonSituations":"Employees offboarded but their service keys still embedded in cron jobs/notebooks; SCIM soft-delete semantics marking users inactive; test users disabled in the IdP during access reviews; ordering issue where SCIM deactivates before keys are rotated.","solutions":["Re-activate the user in the IdP so SCIM sync sets `scim_active` true again (if the user should keep access)","If the user should stay gone, migrate the workload to a new key owned by an active user/team (`/key/generate` with a valid user_id)","As a proxy admin, clear/flip `metadata.scim_active` for the user record if SCIM state is stale and the deactivation was erroneous"],"exampleFix":"# before: cron uses departed employee's key -> 'deactivated via SCIM'\nclient = OpenAI(base_url=..., api_key=os.environ[\"DEPARTED_USER_KEY\"])\n\n# after: key owned by team/service identity\nnew_key = admin.post(\"/key/generate\", json={\"team_id\": \"svc-team\"}).json()[\"key\"]\nclient = OpenAI(base_url=..., api_key=new_key)","handlingStrategy":"try-catch","validationCode":"# before lending a user-owned key to automation, check the user is SCIM-active\nuser = admin.get(f\"/user/info?user_id={uid}\").json()\nif user.get(\"metadata\", {}).get(\"scim_active\") is False:\n    raise PermissionError(f\"user {uid} SCIM-deactivated; their keys are unusable\")","typeGuard":"def user_is_scim_active(user: dict) -> bool:\n    return user.get(\"metadata\", {}).get(\"scim_active\") is not False","tryCatchPattern":"try:\n    client.chat.completions.create(...)\nexcept Exception as e:\n    if \"deactivated via SCIM\" in str(e):\n        rotate_to_team_key()  # move workload to an active identity's key\n        raise UserDeactivated from e\n    raise","preventionTips":["Bind long-running services to team/service keys, not individual user keys","Coordinate SCIM offboarding with key rotation in the same runbook","Surface SCIM deactivation errors to oncall, not just logs"],"tags":["auth","scim","user-management","offboarding","litellm"],"backgroundTag":"user-account-deactivated","analyzedSha":"77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8","analyzedAt":"2026-08-18T11:44:31.656Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}