moghtech/komodo · error · anyhow::Error

Service Users cannot add additional login methods

Error message

Service Users cannot add additional login methods

What it means

set_user_hashed_password inspects the user's UserConfig before building the update document: if the user is a Service user (machine account), no password login method may be attached to it, so the update is rejected with this error. It is a business-rule guard protecting the invariant that Service accounts authenticate via other means, not passwords.

Solutions

  1. Only call password updates for users with UserConfig::Local
  2. Filter service users out in admin reset flows (match user.config before calling)
  3. If the account should have a password, it was mis-provisioned — recreate it as a Local user
  4. Use the service account's intended credential mechanism instead of a password

Example fix

// before
for user in users { db.set_user_password(&user, tmp_pw).await?; }
// after
for user in users {
  if matches!(user.config, UserConfig::Local { .. }) {
    db.set_user_password(&user, tmp_pw).await?;
  }
}
Defensive patterns

Strategy: type-guard

Validate before calling

fn can_set_password(user: &User) -> bool {
  matches!(user.config, UserConfig::Local { .. })
}

Type guard

fn is_local_user(user: &User) -> bool {
  matches!(user.config, UserConfig::Local { .. })
}

Try / catch

match db.set_user_password(&user, &pw).await {
  Err(e) if e.to_string().contains("Service Users cannot") => {
    log::info!("skipped password reset for service account {}", user.id);
    Ok(())
  }
  other => other,
}

Prevention

When it happens

Trigger: Calling set_user_hashed_password (directly or via set_user_password) on a User whose config is UserConfig::Service.

Common situations: Admin password-reset UI iterating over all users including service accounts; scripts that reset passwords for automation users; mixing up a service account's ID with a human user's ID.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


AI-assisted analysis of moghtech/komodo@780ac68b99 (2026-09-08). Data as JSON: /api/errors/bd40b2a486c089f3. Report an issue: GitHub.

Appendix: source

Thrown at lib/database/src/lib.rs:138

    password: &str,
  ) -> anyhow::Result<()> {
    if password.is_empty() {
      return Err(anyhow!("Password cannot be empty."));
    }
    let hashed_password =
      hash_password(password).context("Failed to hash password")?;
    self.set_user_hashed_password(user, hashed_password).await
  }

  /// Updates a user's password using a DB call.
  pub async fn set_user_hashed_password(
    &self,
    user: &User,
    hashed_password: String,
  ) -> anyhow::Result<()> {
    let update = match user.config {
      UserConfig::Service { .. } => {
        return Err(anyhow!(
          "Service Users cannot add additional login methods"
        ));
      }
      // Update a primary 'Local' user's password directly.
      UserConfig::Local { .. } => {
        doc! {
          "$set": {
            "config.data.password": hashed_password
          }
        }
      }
      // Update User with Local password as an entry in 'additional_logins'
      _ => {
        let bson = to_bson(&UserConfig::Local {
          password: hashed_password,
        })
        .context("Failed to serialize login method to bson")?;
        doc! {

View on GitHub (pinned to 780ac68b99)