signalapp/Signal-Server · error · BadRequestException

Operation requires unauthenticated access

Error message

Operation requires unauthenticated access

What it means

requireNotAuthenticated enforces that username-hash lookup, username-link lookup, and account-existence checks are only performed anonymously. If the request carries credentials for an authenticated device, the server throws BadRequestException('Operation requires unauthenticated access') to prevent accounts from probing other usernames while logged in.

Solutions

  1. Issue these lookups from an unauthenticated client that does not attach the Authorization header.
  2. Configure the HTTP client to strip credentials for anonymous endpoints.
  3. Use a separate, credential-free client instance for username lookup/account-existence probes.

Example fix

// before
httpClient.get("/v1/accounts/username_hash/" + hash, withAuthHeader(token));
// after
anonymousHttpClient.get("/v1/accounts/username_hash/" + hash); // no Authorization header
Defensive patterns

Strategy: validation

Validate before calling

if (request.headers['authorization']) throw new Error('strip Authorization header for anonymous lookups');

Type guard

const isAnonymousRequest = (opts) => opts.headers && !('Authorization' in opts.headers);

Try / catch

try { return await lookupUsernameHash(hash); } catch (e) { if (e.status === 400 && e.message.includes('unauthenticated access')) { return anonymousClient.lookupUsernameHash(hash); } throw e; }

Prevention

When it happens

Trigger: Calling GET /v1/accounts/username_hash/{hash}, GET /v1/accounts/username_link/{uuid}, or the account-exists endpoint while including an Authorization header / authenticated device credentials.

Common situations: Shared HTTP client that automatically attaches the auth token to every request, SDK wrappers that inject credentials globally, or tests replaying an authenticated session for anonymous lookups.

Understand the failure class

Related errors


AI-assisted analysis of signalapp/Signal-Server@100ab61c82 (2026-09-09). Data as JSON: /api/errors/4951887998e8903c. Report an issue: GitHub.

Appendix: source

Thrown at service/src/main/java/org/whispersystems/textsecuregcm/controllers/AccountController.java:542

  }

  private void clearUsernameLink(final UUID accountIdentifier) {
    updateUsernameLink(accountIdentifier, null, null);
  }

  private void updateUsernameLink(
      final UUID accountIdentifier,
      @Nullable final UUID usernameLinkHandle,
      @Nullable final byte[] encryptedUsername) {
    if ((encryptedUsername == null) ^ (usernameLinkHandle == null)) {
      throw new IllegalStateException("Both or neither arguments must be null");
    }
    accounts.update(accountIdentifier, a -> a.setUsernameLinkDetails(usernameLinkHandle, encryptedUsername));
  }

  private void requireNotAuthenticated(final Optional<AuthenticatedDevice> authenticatedAccount) {
    if (authenticatedAccount.isPresent()) {
      throw new BadRequestException("Operation requires unauthenticated access");
    }
  }
}

View on GitHub (pinned to 100ab61c82)