apache/dolphinscheduler · error · ServiceException

10009

10009

Error message

os tenant code {tenantCode} already exists

What it means

Status.OS_TENANT_CODE_EXIST from createTenantValid: a tenant with the given tenantCode already exists in t_ds_tenant (checkTenantExists returns true). Tenant codes are unique keys mapping to OS users, so duplicates are rejected.

Source

Thrown at dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/TenantServiceImpl.java:94

    private UserDao userDao;

    @Autowired
    private QueueService queueService;

    /**
     * Check the tenant new object valid or not
     *
     * @param tenant The tenant object want to create
     */
    private void createTenantValid(Tenant tenant) throws ServiceException {
        if (StringUtils.isEmpty(tenant.getTenantCode())) {
            throw new ServiceException(Status.REQUEST_PARAMS_NOT_VALID_ERROR, tenant.getTenantCode());
        } else if (StringUtils.length(tenant.getTenantCode()) > TENANT_FULL_NAME_MAX_LENGTH) {
            throw new ServiceException(Status.TENANT_FULL_NAME_TOO_LONG_ERROR);
        } else if (!RegexUtils.isValidLinuxUserName(tenant.getTenantCode())) {
            throw new ServiceException(Status.CHECK_OS_TENANT_CODE_ERROR);
        } else if (checkTenantExists(tenant.getTenantCode())) {
            throw new ServiceException(Status.OS_TENANT_CODE_EXIST, tenant.getTenantCode());
        }
    }

    /**
     * Check tenant update object valid or not
     *
     * @param existsTenant The exists queue object
     * @param updateTenant The queue object want to update
     */
    private void updateTenantValid(Tenant existsTenant, Tenant updateTenant) throws ServiceException {
        // Check the exists tenant
        if (Objects.isNull(existsTenant)) {
            log.error("Tenant does not exist.");
            throw new ServiceException(Status.TENANT_NOT_EXIST);
        }
        // Check the update tenant parameters
        else if (StringUtils.isEmpty(updateTenant.getTenantCode())) {
            throw new ServiceException(Status.REQUEST_PARAMS_NOT_VALID_ERROR, updateTenant.getTenantCode());

View on GitHub (pinned to 02eac45a1b)

Solutions

  1. Query the tenant list first and reuse the existing tenant instead of creating a new one.
  2. Make scripts idempotent: check existence (or catch this error) before create.
  3. Serialize tenant provisioning (single sync job, distributed lock) to avoid the create race.
  4. If the old tenant is stale, delete it first via the tenant management API, then recreate.

Example fix

// before
tenantService.createTenant(loginUser, "ds_tenant", "desc", 1); // throws if exists
// after
if (!tenantService.checkTenantExists("ds_tenant")) {
    tenantService.createTenant(loginUser, "ds_tenant", "desc", 1);
}
Defensive patterns

Strategy: try-catch

Validate before calling

if (tenantService.checkTenantExists(tenantCode)) {
    return; // tenant already provisioned
}

Try / catch

try {
    tenantService.createTenant(loginUser, tenantCode, desc, queueId);
} catch (ServiceException e) {
    if (e.getCode() == 10009) { /* already exists: reuse it, don't fail the script */ }
}

Prevention

When it happens

Trigger: POST /tenants with a tenantCode that an earlier create already registered; createTenantIfNotExists races when two requests create the same tenant concurrently.

Common situations: Rerunning idempotency-broken bootstrap scripts; LDAP/SSO sync jobs running on multiple nodes simultaneously; admins recreating a tenant after a partially failed setup without checking existing tenants.

Understand the failure class

Background: "already exists" / EEXIST / FileAlreadyExistsException: what the 'file already exists' error means and how to fix it — this error's family across 37 libraries.

Related errors


AI-assisted analysis of apache/dolphinscheduler@02eac45a1b (2026-09-06). Data as JSON: /api/errors/72b980e5e195cc0e. Report an issue: GitHub.