{"record":{"id":"a9981328edb8fc79","repo":"SonarSource/sonarqube","slug":"invalid-personal-access-token-a99813","errorCode":null,"errorMessage":"Invalid personal access token","messagePattern":"Invalid personal access token","errorType":"http","errorClass":"BitbucketServerException","httpStatus":401,"severity":"error","filePath":"server/sonar-alm-client/src/main/java/org/sonar/alm/client/bitbucketserver/BitbucketServerRestClient.java","lineNumber":188,"sourceCode":"  }\n\n  protected static void validateResponseBody(boolean isSuccessful, String bodyString) {\n    if (isSuccessful) {\n      try {\n        buildGson().fromJson(bodyString, Object.class);\n      } catch (JsonParseException e) {\n        LOG.info(UNEXPECTED_RESPONSE_FROM_BITBUCKET_SERVER + \" : [{}]\", bodyString);\n        throw new IllegalArgumentException(UNEXPECTED_RESPONSE_FROM_BITBUCKET_SERVER, e);\n      }\n    }\n  }\n\n  protected static void handleHttpErrorIfAny(boolean isSuccessful, int httpCode, String bodyString) {\n    if (!isSuccessful) {\n      String errorMessage = getErrorMessage(bodyString);\n      LOG.info(UNABLE_TO_CONTACT_BITBUCKET_SERVER + \": {} {}\", httpCode, errorMessage);\n      if (httpCode == HTTP_UNAUTHORIZED) {\n        throw new BitbucketServerException(HTTP_UNAUTHORIZED, \"Invalid personal access token\");\n      } else if (httpCode == HTTP_NOT_FOUND) {\n        throw new BitbucketServerException(HTTP_NOT_FOUND, \"Error 404. The requested Bitbucket server is unreachable.\");\n      }\n      throw new IllegalArgumentException(UNABLE_TO_CONTACT_BITBUCKET_SERVER);\n    }\n  }\n\n  protected static boolean equals(@Nullable MediaType first, @Nullable MediaType second) {\n    String s1 = convertMediaTypeToString(first);\n    String s2 = convertMediaTypeToString(second);\n    return s1 != null && s1.equals(s2);\n  }\n\n  private static String convertMediaTypeToString(@Nullable MediaType mediaType) {\n    return Optional.ofNullable(mediaType)\n      .map(MediaType::toString)\n      .map(s -> s.toLowerCase(ENGLISH).replace(\" \", \"\"))\n      .orElse(null);","sourceCodeStart":170,"sourceCodeEnd":206,"githubUrl":"https://github.com/SonarSource/sonarqube/blob/184c821202192afc1c599fc912d0889b69fffa53/server/sonar-alm-client/src/main/java/org/sonar/alm/client/bitbucketserver/BitbucketServerRestClient.java#L170-L206","documentation":"handleHttpErrorIfAny inspects non-2xx responses. On HTTP 401 (Unauthorized) it throws BitbucketServerException(401, 'Invalid personal access token'), meaning Bitbucket rejected the configured personal access token's credentials. Other codes map to 404 'server unreachable' or a generic IllegalArgumentException.","triggerScenarios":"Calling any Bitbucket REST endpoint when the PAT bound to the ALM setting is revoked, expired, mistyped, lacks the required permissions (e.g. REPO_READ), or belongs to a disabled user — Bitbucket returns 401 and this exception is raised by getBodyString.","commonSituations":"PAT expired per Bitbucket's token expiry policy; token rotated/revoked after a user left; token pasted with whitespace or from the wrong Bitbucket instance; user account deactivated; token created without repository read permission.","solutions":["Generate a new personal access token in Bitbucket (with REPO_READ or required project permissions) and update it in the SonarQube ALM integration setting.","Verify the token belongs to a still-active user on the Bitbucket instance referenced by serverUrl.","Confirm the token was not truncated/prefixed — paste it again without whitespace.","Test the token directly: curl -H 'Authorization: Bearer <token>' <serverUrl>/rest/api/1.0/inbox."],"exampleFix":"// before: almSetting pat = expired/revoked token\n// after: create a new PAT in Bitbucket > Account > Personal access tokens and update the SonarQube ALM setting","handlingStrategy":"try-catch","validationCode":"// Validate the token before configuring:\ncurl -s -o /dev/null -w \"%{http_code}\" -H \"Authorization: Bearer <pat>\" <serverUrl>/rest/api/1.0/inbox\n// expect 200; 401 means the token is invalid/expired/lacks permissions","typeGuard":null,"tryCatchPattern":"try {\n  // call client\n} catch (BitbucketServerException e) {\n  if (e.getHttpStatusCode() == 401) {\n    // prompt admin to refresh the personal access token in ALM settings\n  }\n}","preventionTips":["Set calendar reminders for PAT expiry; Bitbucket enforces token expiration dates.","Grant the token's user only the required permissions (REPO_READ) and keep the account active.","Validate the token with a curl pre-flight check whenever ALM settings change.","Use a dedicated service account token instead of an individual user's token to survive employee offboarding."],"tags":["java","bitbucket-server","authentication","http-401"],"backgroundTag":"authentication-required","analyzedSha":"184c821202192afc1c599fc912d0889b69fffa53","analyzedAt":"2026-09-09T12:23:51.573Z","contentChangedAt":"2026-09-09T12:23:51.573Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}