{"record":{"id":"044f7ab278fe01e5","repo":"signalapp/Signal-Server","slug":"400-bad-request-invalid-arguments","errorCode":null,"errorMessage":"400 Bad Request (INVALID_ARGUMENTS)","messagePattern":"400 Bad Request \\(INVALID_ARGUMENTS\\)","errorType":"http","errorClass":"BadRequestException","httpStatus":400,"severity":"error","filePath":"service/src/main/java/org/whispersystems/textsecuregcm/controllers/SubscriptionController.java","lineNumber":440,"sourceCode":"      final CustomerAwareSubscriptionPaymentProcessor manager = getCustomerAwareProcessor(\n          processorCustomer.processor());\n      subscriptionManager.updateSubscriptionLevelForCustomer(subscriberCredentials, record, manager, level,\n          currency, idempotencyKey, subscriptionTemplateId, this::subscriptionsAreSameType);\n      return new SetSubscriptionLevelSuccessResponse(level);\n    } catch (SubscriptionInvalidLevelException e) {\n      throw new BadRequestException(Response.status(Response.Status.BAD_REQUEST)\n          .entity(new SubscriptionController.SetSubscriptionLevelErrorResponse(List.of(\n              new SubscriptionController.SetSubscriptionLevelErrorResponse.Error(\n                  SubscriptionController.SetSubscriptionLevelErrorResponse.Error.Type.UNSUPPORTED_LEVEL,\n                  null))))\n          .build());\n    } catch (SubscriptionPaymentRequiresActionException e) {\n      throw new BadRequestException(Response.status(Response.Status.BAD_REQUEST)\n          .entity(new SetSubscriptionLevelErrorResponse(List.of(new SetSubscriptionLevelErrorResponse.Error(\n              SetSubscriptionLevelErrorResponse.Error.Type.PAYMENT_REQUIRES_ACTION, null))))\n          .build());\n    } catch (SubscriptionInvalidArgumentsException e) {\n      throw new BadRequestException(Response.status(Response.Status.BAD_REQUEST)\n          .entity(new SetSubscriptionLevelErrorResponse(List.of(new SetSubscriptionLevelErrorResponse.Error(\n              SetSubscriptionLevelErrorResponse.Error.Type.INVALID_ARGUMENTS, e.getMessage()))))\n          .build());\n    }\n  }\n\n  public boolean subscriptionsAreSameType(long level1, long level2) {\n    return subscriptionConfiguration.getSubscriptionLevel(level1).type()\n        == subscriptionConfiguration.getSubscriptionLevel(level2).type();\n  }\n\n  @POST\n  @Path(\"/{subscriberId}/appstore/{originalTransactionId}\")\n  @Consumes(MediaType.APPLICATION_JSON)\n  @Produces(MediaType.APPLICATION_JSON)\n  @Operation(summary = \"Set app store subscription\", description = \"\"\"\n  Set an originalTransactionId that represents an IAP subscription made with the app store.\n  ","sourceCodeStart":422,"sourceCodeEnd":458,"githubUrl":"https://github.com/signalapp/Signal-Server/blob/100ab61c82627582c867d19e1c0561ba2781e927/service/src/main/java/org/whispersystems/textsecuregcm/controllers/SubscriptionController.java#L422-L458","documentation":"setSubscriptionLevel catches SubscriptionInvalidArgumentsException and returns a 400 with error type INVALID_ARGUMENTS, carrying the underlying exception message: the combination of arguments (level/currency/processor/template/payment method) is invalid for the subscription.","triggerScenarios":"Requesting a level change whose arguments don't fit the existing subscription (e.g. switching subscription type incompatibly, mismatched currency, missing required fields).","commonSituations":"Changing to a level backed by a different payment processor without creating a matching payment method; sending a currency not supported for that level.","solutions":["Read e.getMessage()/the INVALID_ARGUMENTS error text to see which argument is invalid and correct it","Ensure the existing subscription's type matches the requested level (the subscriptionsAreSameType check)","Create an appropriate payment method for the target processor before changing level"],"exampleFix":"// before — mismatched currency vs existing subscription\n{ \"level\": 5, \"currency\": \"gbp\" }\n// after\n{ \"level\": 5, \"currency\": \"usd\" }","handlingStrategy":"try-catch","validationCode":"// ensure the new level's type matches the existing subscription\nconst sameType = existingSubscription.templateType === templateFor(level, currency, processor);\nif (!sameType) throw new Error('Level change would switch subscription type');","typeGuard":null,"tryCatchPattern":"try { await setSubscriptionLevel(level, currency); } catch (e) { if (e.errorType === 'INVALID_ARGUMENTS') { console.warn(e.message); await fixAndRetry(e); } else { throw e; } }","preventionTips":["Keep currency, level, and processor consistent with the existing subscription","Read the INVALID_ARGUMENTS message to identify the offending field"],"tags":["http-400","bad-request","subscriptions","validation"],"backgroundTag":"invalid-argument-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"}