{"record":{"id":"7e0fd9424e79c20e","repo":"signalapp/Signal-Server","slug":"invalid-action","errorCode":null,"errorMessage":"Invalid action: ","messagePattern":"Invalid action: ","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":400,"severity":"error","filePath":"service/src/main/java/org/whispersystems/textsecuregcm/controllers/DeviceCheckController.java","lineNumber":273,"sourceCode":"    // The request assertion was validated, execute it\n    switch (request.assertionRequest().action()) {\n      case BACKUP -> backupAuthManager.extendBackupVoucher(\n              account,\n              new Account.BackupVoucher(BackupLevel.PAID.getValue(), clock.instant().plus(backupRedemptionDuration)));\n    }\n  }\n\n  public enum Action {\n    BACKUP;\n\n    @JsonCreator\n    public static Action fromString(final String action) {\n      for (final Action a : Action.values()) {\n        if (a.name().toLowerCase(Locale.ROOT).equals(action)) {\n          return a;\n        }\n      }\n      throw new IllegalArgumentException(\"Invalid action: \" + action);\n    }\n  }\n\n  public record AssertionRequest(\n      @Schema(description = \"The challenge retrieved at `GET /v1/devicecheck/assert`\")\n      String challenge,\n      @Schema(description = \"The type of action you'd like to perform with this assert\",\n          allowableValues = {\"backup\"}, implementation = String.class)\n      Action action) {}\n\n  /*\n   * Parses the base64 encoded AssertionRequest, but preserves the rawJson as well\n   */\n  public record AssertionRequestWrapper(AssertionRequest assertionRequest, byte[] rawJson) {\n\n    public static AssertionRequestWrapper fromString(String requestBase64) throws IOException {\n      final byte[] requestJson = Base64.getUrlDecoder().decode(requestBase64);\n      final AssertionRequest requestData = SystemMapper.jsonMapper().readValue(requestJson, AssertionRequest.class);","sourceCodeStart":255,"sourceCodeEnd":291,"githubUrl":"https://github.com/signalapp/Signal-Server/blob/100ab61c82627582c867d19e1c0561ba2781e927/service/src/main/java/org/whispersystems/textsecuregcm/controllers/DeviceCheckController.java#L255-L291","documentation":"DeviceCheckController.Action.fromString converts a request-supplied string into the Action enum by case-insensitive name comparison; if no enum constant matches it throws IllegalArgumentException(\"Invalid action: \" + action). It is a strict input-validation guard for the action query/body parameter.","triggerScenarios":"Calling the devicecheck endpoint with an action parameter that is not exactly one of the Action enum names (case-insensitive), e.g. 'attestation' instead of 'attest', a typo, trailing whitespace, or an empty string.","commonSituations":"API consumers guessing action values instead of using documented enum names; version drift where the client sends an action the server build does not define; URL encoding or whitespace corruption of the parameter.","solutions":["Send one of the exact supported action values (case-insensitive match on the enum names, e.g. 'attest' or 'assert')","Trim and normalize the action string on the client before sending","Check the server's DeviceCheckController.Action enum for the list of valid values for your server version","Upgrade the client or server if a newly added action is missing from one side"],"exampleFix":"// before\nclient.deviceCheckAction(\"attestation\"); // IllegalArgumentException: Invalid action: attestation\n// after\nclient.deviceCheckAction(\"attest\"); // matches Action.ATTEST via fromString","handlingStrategy":"validation","validationCode":"final Set<String> valid = Arrays.stream(Action.values()).map(a -> a.name().toLowerCase(Locale.ROOT)).collect(Collectors.toSet());\nif (!valid.contains(action.trim().toLowerCase(Locale.ROOT))) {\n  throw new IllegalArgumentException(\"action must be one of \" + valid);\n}","typeGuard":null,"tryCatchPattern":"try {\n  client.deviceCheckAction(action);\n} catch (WebApplicationException | IllegalArgumentException e) {\n  if (String.valueOf(e).contains(\"Invalid action\")) {\n    // fall back to a supported action or surface a config error\n  } else throw e;\n}","preventionTips":["Use enum constants client-side, never hand-typed strings","Trim/lowercase action values before sending","Keep client enum in sync with server enum version"],"tags":["input-validation","enum","signal-server"],"backgroundTag":"invalid-enum-value","analyzedSha":"100ab61c82627582c867d19e1c0561ba2781e927","analyzedAt":"2026-09-09T13:29:47.883Z","contentChangedAt":"2026-09-09T13:29:47.883Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}