apache/hadoop · error · IOException

Credential {} already exists in {}

Error message

Credential {} already exists in {}

What it means

UserProvider (the in-memory user:// provider backed by the UGI Credentials object) rejects createCredentialEntry() when the alias already has a secret key stored. Unlike file-backed stores, this state lives per-JVM/session, and entries often get there implicitly when credentials are copied from other providers into the job's Credentials.

Source

Thrown at hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/alias/UserProvider.java:69

    return true;
  }

  @Override
  public synchronized CredentialEntry getCredentialEntry(String alias) {
    byte[] bytes = credentials.getSecretKey(new Text(alias));
    if (bytes == null) {
      return null;
    }
    return new CredentialEntry(
        alias, new String(bytes, StandardCharsets.UTF_8).toCharArray());
  }

  @Override
  public synchronized CredentialEntry createCredentialEntry(String name, char[] credential) 
      throws IOException {
    Text nameT = new Text(name);
    if (credentials.getSecretKey(nameT) != null) {
      throw new IOException("Credential " + name + 
          " already exists in " + this);
    }
    credentials.addSecretKey(new Text(name), 
        new String(credential).getBytes(StandardCharsets.UTF_8));
    return new CredentialEntry(name, credential);
  }

  @Override
  public synchronized void deleteCredentialEntry(String name) throws IOException {
    byte[] cred = credentials.getSecretKey(new Text(name));
    if (cred != null) {
      credentials.removeSecretKey(new Text(name));
    }
    else {
      throw new IOException("Credential " + name + 
          " does not exist in " + this);
    }
  }

View on GitHub (pinned to 2add963021)

Solutions

  1. Check first: provider.getCredentialEntry(name) != null -> delete or skip
  2. For overwrite semantics go to the underlying object: ugi.getCredentials().addSecretKey(new Text(name), bytes) (replaces silently)
  3. Filter providers by scheme - skip user:// when your intent is persistent storage, use jceks/localjceks

Example fix

// before
for (CredentialProvider p : CredentialProviderFactory.getProviders(conf)) {
  p.createCredentialEntry(name, secret);   // user:// already has it -> IOException
}

// after
CredentialEntry existing = p.getCredentialEntry(name);
if (existing == null) {
  p.createCredentialEntry(name, secret);
}
Defensive patterns

Strategy: validation

Validate before calling

// Skip or delete before create on the user:// provider
if (provider instanceof UserProvider) {
  if (provider.getCredentialEntry(name) != null) {
    provider.deleteCredentialEntry(name);   // or skip if present
  }
}
provider.createCredentialEntry(name, credential);

Type guard

boolean isUserProvider(CredentialProvider p) {
  return p instanceof org.apache.hadoop.security.alias.UserProvider;
}

Try / catch

try {
  provider.createCredentialEntry(name, credential);
} catch (IOException ex) {
  if (ex.getMessage() != null && ex.getMessage().contains("already exists")) {
    ugi.getCredentials().addSecretKey(new Text(name),
        new String(credential).getBytes(StandardCharsets.UTF_8)); // overwrite semantics
  } else { throw ex; }
}

Prevention

When it happens

Trigger: Calling userProvider.createCredentialEntry(name, cred) twice; the alias was already added via credentials.addSecretKey() or copied from a jceks provider into the same UGI Credentials; re-running bootstrap code in one session.

Common situations: Retrieving all providers via CredentialProviderFactory.getProviders() (user:// is always first) and trying to create into it without checking; tests that create fixtures repeatedly; migration code that copies then re-creates.

Related errors


AI-assisted analysis of apache/hadoop@2add963021 (2026-08-22). Data as JSON: /api/errors/d414cd5dd1085d48. Report an issue: GitHub.