{"record":{"id":"1f1fdf9621c52d01","repo":"infiniflow/ragflow","slug":"tenant-tenant-id-has-no-access-to-provider-owned","errorCode":null,"errorMessage":"Tenant {tenant_id} has no access to provider owned by tenant {provider_obj.tenant_id}.","messagePattern":"Tenant (.+?) has no access to provider owned by tenant (.+?)\\.","errorType":"exception","errorClass":"LookupError","httpStatus":null,"severity":"error","filePath":"api/db/joint_services/tenant_model_service.py","lineNumber":449,"sourceCode":"                params[id_field] = resolve_model_id(tenant_id, model_type, params[name_field])\n            except LookupError:\n                logger.debug(\"Could not resolve %s → %s for tenant %s, skipping\", name_field, id_field, tenant_id)\n    return params\n\n\ndef get_api_key(tenant_id: str, model_name: str):\n    # Try direct model ID (UUID) lookup first\n    exist, model_obj = TenantModelService.get_by_id(model_name)\n    if exist:\n        # Verify tenant ownership through the provider chain\n        ok, provider_obj = TenantModelProviderService.get_by_id(model_obj.provider_id)\n        if not ok:\n            raise LookupError(f\"Provider id={model_obj.provider_id} not found for model {model_name}.\")\n        if tenant_id != provider_obj.tenant_id:\n            joined_tenants = TenantService.get_joined_tenants_by_user_id(tenant_id)\n            joined_tenant_ids = [t[\"tenant_id\"] for t in joined_tenants]\n            if provider_obj.tenant_id not in joined_tenant_ids:\n                raise LookupError(f\"Tenant {tenant_id} has no access to provider owned by tenant {provider_obj.tenant_id}.\")\n\n        exist_inst, instance_obj = TenantModelInstanceService.get_by_id(model_obj.instance_id)\n        if not exist_inst:\n            logger.warning(\n                \"Direct-ID resolution: instance not found | tenant_id=%s model_id=%s instance_id=%s\",\n                tenant_id,\n                model_name,\n                model_obj.instance_id,\n            )\n            raise LookupError(f\"Instance {model_obj.instance_id} not found for model {model_name}.\")\n        logger.debug(\n            \"Direct-ID resolution: resolved | tenant_id=%s model_id=%s instance_id=%s\",\n            tenant_id,\n            model_name,\n            model_obj.instance_id,\n        )\n        return instance_obj.api_key\n","sourceCodeStart":431,"sourceCodeEnd":467,"githubUrl":"https://github.com/infiniflow/ragflow/blob/554fb1133ac3861732235ad9c377eb5e0a770665/api/db/joint_services/tenant_model_service.py#L431-L467","documentation":"LookupError raised by get_api_key (direct-ID path) as a tenant-access guard: the model's provider is owned by provider_obj.tenant_id, and the requesting tenant_id is neither the owner nor one of its joined teams (checked via TenantService.get_joined_tenants_by_user_id). It prevents a tenant from extracting another tenant's API key through a guessed or shared model id.","triggerScenarios":"Passing a model id owned by tenant A while authenticating as tenant B, where B has not joined a team of A's; typical when ids are copied between accounts or an integration hardcodes one tenant's model id.","commonSituations":"Multi-tenant deployments where a user pastes a colleague's model id; SDK scripts reusing a recorded id after switching login; misconfigured tenant scoping in a proxy.","solutions":["Use a model id (or model@provider name) that belongs to the authenticated tenant.","If sharing is intended, have the consumer tenant join the provider owner's team so get_joined_tenants_by_user_id includes the owner.","Or add the same provider under the consuming tenant with its own key and reference that tenant's model.","Never hardcode cross-tenant ids; resolve names per tenant at runtime."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"exist, m = TenantModelService.get_by_id(model_id)\nif exist:\n    _, p = TenantModelProviderService.get_by_id(m.provider_id)\n    if tenant_id != p.tenant_id:\n        joined = {t[\"tenant_id\"] for t in TenantService.get_joined_tenants_by_user_id(tenant_id)}\n        if p.tenant_id not in joined:\n            raise PermissionError(\"Model belongs to another tenant\")","typeGuard":null,"tryCatchPattern":"try:\n    key = get_api_key(tenant_id, model_id)\nexcept LookupError as e:\n    if \"has no access\" in str(e):\n        # authorization failure: stop, do not retry with same credentials\n        raise PermissionError(str(e))","preventionTips":["Resolve model references per tenant at runtime; never hardcode another tenant's ids.","For team sharing, join the owner's team first so joined-tenant checks pass.","Treat this error as a security signal — audit where the foreign id came from."],"tags":["authorization","multi-tenant","api-key","ragflow"],"backgroundTag":null,"analyzedSha":"554fb1133ac3861732235ad9c377eb5e0a770665","analyzedAt":"2026-08-15T09:20:16.380Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}