{"record":{"id":"d2b7637726f39b4c","repo":"YunaiV/ruoyi-vue-pro","slug":"tenantcontextholder","errorCode":null,"errorMessage":"TenantContextHolder 不存在租户编号！可参考文档：{}","messagePattern":"TenantContextHolder 不存在租户编号！可参考文档：(.+?)","errorType":"exception","errorClass":"NullPointerException","httpStatus":null,"severity":"error","filePath":"yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/context/TenantContextHolder.java","lineNumber":40,"sourceCode":"\n    /**\n     * 获得租户编号\n     *\n     * @return 租户编号\n     */\n    public static Long getTenantId() {\n        return TENANT_ID.get();\n    }\n\n    /**\n     * 获得租户编号。如果不存在，则抛出 NullPointerException 异常\n     *\n     * @return 租户编号\n     */\n    public static Long getRequiredTenantId() {\n        Long tenantId = getTenantId();\n        if (tenantId == null) {\n            throw new NullPointerException(\"TenantContextHolder 不存在租户编号！可参考文档：\"\n                + DocumentEnum.TENANT.getUrl());\n        }\n        return tenantId;\n    }\n\n    public static void setTenantId(Long tenantId) {\n        TENANT_ID.set(tenantId);\n    }\n\n    public static void setIgnore(Boolean ignore) {\n        IGNORE.set(ignore);\n    }\n\n    /**\n     * 当前是否忽略租户\n     *\n     * @return 是否忽略\n     */","sourceCodeStart":22,"sourceCodeEnd":58,"githubUrl":"https://github.com/YunaiV/ruoyi-vue-pro/blob/0418084e222612af2fc1141f566af454f9236ab1/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/context/TenantContextHolder.java#L22-L58","documentation":"yudao's TenantContextHolder stores the current tenant id in a ThreadLocal. getRequiredTenantId() throws NullPointerException when no tenant is set (e.g. a background job, async thread, or a request that skipped the tenant filter). It is a hard guard: code that needs a tenant must not run without one.","triggerScenarios":"Calling getRequiredTenantId() from a @Async method, scheduled task, message listener, or new thread that did not inherit the tenant context; a request to a path excluded from TenantContextWebFilter; tenant filter ran but set no default tenant.","commonSituations":"Async/scheduled jobs that forget to propagate tenant; web-filter ignore-rules for a tenant-required endpoint; misconfigured TenantContextHolder default.","solutions":["Propagate the tenant into async/scheduled threads (use yudao's tenant-aware task decorator or set TenantContextHolder.setTenantId(...) explicitly).","Verify the endpoint is not excluded from the tenant filter; remove it from ignore lists if it requires a tenant.","If the code legitimately has no tenant (cross-tenant/system task), use TenantUtils.executeIgnore(...) or getTenantId() (nullable) instead of getRequiredTenantId()."],"exampleFix":"// before: background job with no tenant context\nLong tenantId = TenantContextHolder.getRequiredTenantId();\n// after: run the job under an explicit/ignored tenant context\nTenantUtils.execute(tenantId, () -> doWork());\n// or, if system-level:\nTenantUtils.executeIgnore(() -> doWork());","handlingStrategy":"validation","validationCode":"Long tid = TenantContextHolder.getTenantId();\nif (tid == null) throw new IllegalStateException(\"No tenant in context; propagate via TenantUtils.execute(tenantId, ...)\");","typeGuard":"static boolean hasTenant() { return TenantContextHolder.getTenantId() != null; }","tryCatchPattern":"try { TenantContextHolder.getRequiredTenantId(); }\ncatch (NullPointerException e) { /* run under TenantUtils.executeIgnore or set explicit tenant */ }","preventionTips":["Propagate tenant context into async/scheduled threads","Use TenantUtils.execute(...) for tenant-required work in background jobs","Audit web-filter ignore lists against tenant-required endpoints"],"tags":["yudao","tenant","threadlocal","context"],"backgroundTag":null,"analyzedSha":"0418084e222612af2fc1141f566af454f9236ab1","analyzedAt":"2026-08-14T00:56:18.412Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}