jeecgboot/JeecgBoot · warning · JeecgBootException

cancelTenant

Error message

cancelTenant

What it means

exitUserTenant throws the literal 'cancelTenant' sentinel when the owner is the sole remaining member (userIdsByTenantId.size() == 1). Like error 209, this is flow-control: it tells the frontend to initiate tenant cancellation rather than a plain exit. JeecgBootException is used as a signal channel.

Source

Thrown at jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/system/service/impl/SysTenantServiceImpl.java:350

   }


    @Override
    public void exitUserTenant(String userId, String username, String tenantId) {
        int tId = Integer.parseInt(tenantId);
        //获取所有租户信息
        List<String> userIdsByTenantId = userTenantMapper.getUserIdsByTenantId(tId);
        //查询当前租户是否为拥有者
        SysTenant sysTenant = tenantMapper.selectById(tId);
        //如果是拥有着
        if (username.equals(sysTenant.getCreateBy())) {
            //判断当前租户信息位数
            if (null != userIdsByTenantId && userIdsByTenantId.size() > 1) {
                //需要指配拥有者
                throw new JeecgBootException("assignedOwen");
            } else if (null != userIdsByTenantId && userIdsByTenantId.size() == 1) {
                //只有拥有者的时候需要去注销租户
                throw new JeecgBootException("cancelTenant");
            } else {
                throw new JeecgBootException("退出租户失败,租户信息已不存在");
            }
        } else {
            //不是拥有者直接删除
            this.leaveTenant(userId, tenantId);
            this.leveUserProcess(userId, tenantId);
        }
    }

    @Override
    public void changeOwenUserTenant(String userId, String tId) {
        //查询当前用户是否存在该租户下
        // 代码逻辑说明: 租户id应该是传过来的,不应该是当前租户的------------
        int tenantId = oConvertUtils.getInt(tId, 0);
        SysTenant sysTenant = tenantMapper.selectById(tenantId);
        if(null == sysTenant){
            throw new JeecgBootException("退出租户失败,不存在此租户");

View on GitHub (pinned to 96fb33f5ec)

Solutions

  1. Catch 'cancelTenant' in the caller and route the user to the tenant-cancellation/deactivation workflow.
  2. Confirm the user genuinely wants to dissolve the tenant before proceeding with cancellation.
  3. If the owner wants to leave but keep the tenant, first transfer ownership (changeOwenUserTenant) to another invited member.
  4. Refactor upstream to return a result code instead of throwing a sentinel string for cleaner control flow.

Example fix

// before
tenantService.exitUserTenant(userId, username, tenantId);

// after
try {
    tenantService.exitUserTenant(userId, username, tenantId);
} catch (JeecgBootException e) {
    if ("cancelTenant".equals(e.getMessage())) {
        return Result.OK("CONFIRM_CANCEL_TENANT", null);
    }
    throw e;
}
Defensive patterns

Strategy: try-catch

Try / catch

try {
    tenantService.exitUserTenant(userId, username, tenantId);
} catch (JeecgBootException e) {
    if ("cancelTenant".equals(e.getMessage())) {
        return Result.OK("CONFIRM_CANCEL_TENANT", null);
    }
    throw e;
}

Prevention

When it happens

Trigger: The tenant creator exits the tenant and no other members remain. Exiting would orphan the tenant, so the system instead requires explicit cancellation/deactivation.

Common situations: Owner is the last member and selects exit; a cleanup script calls exitUserTenant for a single-member tenant; frontend lacks the handler for the 'cancelTenant' sentinel.

Related errors


AI-assisted analysis of jeecgboot/JeecgBoot@96fb33f5ec (2026-08-14). Data as JSON: /api/errors/d58aaa1f9dad165e. Report an issue: GitHub.