YunaiV/ruoyi-vue-pro · error · NullPointerException

TenantContextHolder 不存在租户编号!可参考文档:{}

Error message

TenantContextHolder 不存在租户编号!可参考文档:{}

What it means

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.

Source

Thrown at yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/context/TenantContextHolder.java:40

    /**
     * 获得租户编号
     *
     * @return 租户编号
     */
    public static Long getTenantId() {
        return TENANT_ID.get();
    }

    /**
     * 获得租户编号。如果不存在,则抛出 NullPointerException 异常
     *
     * @return 租户编号
     */
    public static Long getRequiredTenantId() {
        Long tenantId = getTenantId();
        if (tenantId == null) {
            throw new NullPointerException("TenantContextHolder 不存在租户编号!可参考文档:"
                + DocumentEnum.TENANT.getUrl());
        }
        return tenantId;
    }

    public static void setTenantId(Long tenantId) {
        TENANT_ID.set(tenantId);
    }

    public static void setIgnore(Boolean ignore) {
        IGNORE.set(ignore);
    }

    /**
     * 当前是否忽略租户
     *
     * @return 是否忽略
     */

View on GitHub (pinned to 0418084e22)

Solutions

  1. Propagate the tenant into async/scheduled threads (use yudao's tenant-aware task decorator or set TenantContextHolder.setTenantId(...) explicitly).
  2. Verify the endpoint is not excluded from the tenant filter; remove it from ignore lists if it requires a tenant.
  3. If the code legitimately has no tenant (cross-tenant/system task), use TenantUtils.executeIgnore(...) or getTenantId() (nullable) instead of getRequiredTenantId().

Example fix

// before: background job with no tenant context
Long tenantId = TenantContextHolder.getRequiredTenantId();
// after: run the job under an explicit/ignored tenant context
TenantUtils.execute(tenantId, () -> doWork());
// or, if system-level:
TenantUtils.executeIgnore(() -> doWork());
Defensive patterns

Strategy: validation

Validate before calling

Long tid = TenantContextHolder.getTenantId();
if (tid == null) throw new IllegalStateException("No tenant in context; propagate via TenantUtils.execute(tenantId, ...)");

Type guard

static boolean hasTenant() { return TenantContextHolder.getTenantId() != null; }

Try / catch

try { TenantContextHolder.getRequiredTenantId(); }
catch (NullPointerException e) { /* run under TenantUtils.executeIgnore or set explicit tenant */ }

Prevention

When it happens

Trigger: 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.

Common situations: Async/scheduled jobs that forget to propagate tenant; web-filter ignore-rules for a tenant-required endpoint; misconfigured TenantContextHolder default.

Related errors


AI-assisted analysis of YunaiV/ruoyi-vue-pro@0418084e22 (2026-08-14). Data as JSON: /api/errors/d2b7637726f39b4c. Report an issue: GitHub.