{"record":{"id":"3fd574263d79478a","repo":"apolloconfig/apollo","slug":"token-expires-must-be-in-the-future","errorCode":null,"errorMessage":"Token expires must be in the future","messagePattern":"Token expires must be in the future","errorType":"validation","errorClass":"BadRequestException","httpStatus":400,"severity":"error","filePath":"apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/service/UserTokenService.java","lineNumber":393,"sourceCode":"    UserInfo userInfo = userService.findByUserId(userId);\n    if (userInfo == null) {\n      throw BadRequestException.userNotExists(userId);\n    }\n    if (userInfo.getEnabled() != USER_ENABLED) {\n      throw new BadRequestException(\"User is disabled\");\n    }\n  }\n\n  private Date resolveExpires(Date requestedExpires, Date now) {\n    Date expires = requestedExpires;\n    if (expires == null) {\n      Calendar calendar = Calendar.getInstance();\n      calendar.setTime(now);\n      calendar.add(Calendar.DAY_OF_YEAR, portalConfig.userTokenDefaultExpireDays());\n      expires = calendar.getTime();\n    }\n    if (!expires.after(now)) {\n      throw new BadRequestException(\"Token expires must be in the future\");\n    }\n\n    Calendar maxCalendar = Calendar.getInstance();\n    maxCalendar.setTime(now);\n    maxCalendar.add(Calendar.DAY_OF_YEAR, portalConfig.userTokenMaxExpireDays());\n    if (expires.after(maxCalendar.getTime())) {\n      throw new BadRequestException(\"Token expires exceeds max allowed days:%s\",\n          portalConfig.userTokenMaxExpireDays());\n    }\n    return expires;\n  }\n\n  private int resolveRateLimit(Integer rateLimit) {\n    return rateLimit == null ? 0 : rateLimit;\n  }\n\n  private void validateOperator(String operator) {\n    if (StringUtils.isBlank(operator)) {","sourceCodeStart":375,"sourceCodeEnd":411,"githubUrl":"https://github.com/apolloconfig/apollo/blob/d95fc18d112589efc09ddcbe1507047584d55251/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/service/UserTokenService.java#L375-L411","documentation":"Thrown by UserTokenService.resolveExpires() when the requested expiry date exists (is non-null) but is not strictly after the current time (now). If expires is null, a default is computed from portalConfig.userTokenDefaultExpireDays() and this check passes. The check uses Date.after(), so a date equal to now also triggers it. BadRequestException → HTTP 400.","triggerScenarios":"Calling createToken() with request.setExpires() set to a past date, the current instant, or null-but-overridden-after. For example, setting expires to yesterday's timestamp, or a date that has already elapsed by the time the request reaches the server.","commonSituations":"Client clock skew sends a timestamp that the server considers past. Stale cached request retried after the expiry date has passed. Test or migration script hardcoded a fixed past date.","solutions":["Set expires to a date clearly in the future (e.g., now + N days).","If you want the default expiry, leave request.setExpires() as null — the service computes now + userTokenDefaultExpireDays.","Ensure client and server clocks are synchronized (NTP) to avoid false past-date rejections."],"exampleFix":"// before\nrequest.setExpires(yesterday);\n// after\nCalendar cal = Calendar.getInstance();\ncal.add(Calendar.DAY_OF_YEAR, 30);\nrequest.setExpires(cal.getTime());","handlingStrategy":"validation","validationCode":"Date expires = request.getExpires();\nif (expires != null && !expires.after(new Date())) {\n    throw new IllegalArgumentException(\"Expiry must be in the future\");\n}\nuserTokenService.createToken(request, operator);","typeGuard":"static boolean isExpiryInFuture(Date expires) {\n    return expires == null || expires.after(new Date());\n}","tryCatchPattern":"try {\n    userTokenService.createToken(request, operator);\n} catch (BadRequestException e) {\n    if (e.getMessage().contains(\"must be in the future\")) {\n        return Response.status(400).entity(\"Set expiry to a future date or leave it null for default\").build();\n    }\n    throw e;\n}","preventionTips":["Leave expires as null to use the default expiry (userTokenDefaultExpireDays).","Sync client and server clocks via NTP to avoid false past-date errors."],"tags":["apollo-portal","user-token","validation","date","bad-request"],"backgroundTag":null,"analyzedSha":"d95fc18d112589efc09ddcbe1507047584d55251","analyzedAt":"2026-08-14T04:00:05.477Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}