{"record":{"id":"06b0662dc27822bc","repo":"binarywang/WxJava","slug":"setattribute-session-0-has-already-been-inval","errorCode":null,"errorMessage":"setAttribute: Session [{0}] has already been invalidated","messagePattern":"setAttribute: Session \\[(.+?)\\] has already been invalidated","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"warning","filePath":"weixin-java-common/src/main/java/me/chanjar/weixin/common/session/StandardSession.java","lineNumber":118,"sourceCode":"    return Collections.enumeration(names);\n  }\n\n  @Override\n  public void setAttribute(String name, Object value) {\n    // Name cannot be null\n    if (name == null) {\n      throw new IllegalArgumentException(SM.getString(\"sessionImpl.setAttribute.namenull\"));\n    }\n\n    // Null value is the same as removeAttribute()\n    if (value == null) {\n      removeAttribute(name);\n      return;\n    }\n\n    // Validate our current state\n    if (!isValidInternal()) {\n      throw new IllegalStateException(SM.getString(\"sessionImpl.setAttribute.ise\", getIdInternal()));\n    }\n\n    this.attributes.put(name, value);\n\n  }\n\n  @Override\n  public void removeAttribute(String name) {\n    removeAttributeInternal(name);\n  }\n\n  @Override\n  public void invalidate() {\n    if (!isValidInternal()) {\n      throw new IllegalStateException(SM.getString(\"sessionImpl.invalidate.ise\"));\n    }\n\n    // Cause this session to expire","sourceCodeStart":100,"sourceCodeEnd":136,"githubUrl":"https://github.com/binarywang/WxJava/blob/1c43293a3c2c9d7e91304b6d037fb017f680d0c6/weixin-java-common/src/main/java/me/chanjar/weixin/common/session/StandardSession.java#L100-L136","documentation":"StandardSession.setAttribute throws IllegalStateException when the session is invalid (expired/invalidated) at the time of the setAttribute call. Unlike the null-name check (error 34), this validity check runs after the null-value→removeAttribute shortcut but before storing. Writing to an expired session is forbidden to prevent silent data loss.","triggerScenarios":"Calling session.setAttribute(name, value) after session.invalidate() or after the session exceeded maxInactiveInterval.","commonSituations":"A handler stores per-user state on a session that already timed out between two messages; async work writing to a stale session reference; long gaps between user messages exceeding the inactive interval.","solutions":["Check session.isValid() before setAttribute.","Re-acquire a fresh session from the manager/router when the old one is invalid.","Increase maxInactiveInterval if legitimate flows exceed it.","Catch IllegalStateException and re-establish the session for the user."],"exampleFix":"// before\nsession.setAttribute(\"state\", ctx); // throws if session expired\n\n// after\nif (session.isValid()) {\n  session.setAttribute(\"state\", ctx);\n} else {\n  session = sessionManager.createSession(sessionId);\n  session.setAttribute(\"state\", ctx);\n}","handlingStrategy":"validation","validationCode":"if (session.isValid()) {\n  session.setAttribute(name, value);\n} else {\n  session = sessionManager.createSession(sessionId);\n  session.setAttribute(name, value);\n}","typeGuard":"private static boolean sessionAccessible(WxSession s) {\n  return s != null && s.isValid();\n}","tryCatchPattern":"try {\n  session.setAttribute(name, value);\n} catch (IllegalStateException e) {\n  // session expired mid-flow — re-establish and retry\n  session = sessionManager.createSession(sessionId);\n  session.setAttribute(name, value);\n}","preventionTips":["Check session.isValid() before writing state on sessions held across message gaps.","Re-acquire a fresh session when the prior one expired.","Set maxInactiveInterval above the longest expected inter-message gap."],"tags":["session","invalidated","message-router","state"],"backgroundTag":null,"analyzedSha":"1c43293a3c2c9d7e91304b6d037fb017f680d0c6","analyzedAt":"2026-08-14T02:29:11.060Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}