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
- Catch 'cancelTenant' in the caller and route the user to the tenant-cancellation/deactivation workflow.
- Confirm the user genuinely wants to dissolve the tenant before proceeding with cancellation.
- If the owner wants to leave but keep the tenant, first transfer ownership (changeOwenUserTenant) to another invited member.
- 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
- For sole-member tenants, route the owner to the cancellation flow.
- Transfer ownership if the tenant should persist after the owner leaves.
- Handle the 'cancelTenant' sentinel explicitly in the caller.
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.