{"record":{"id":"f9126491d42a75b9","repo":"binarywang/WxJava","slug":"getattributenames-session-already-invalidated","errorCode":null,"errorMessage":"getAttributeNames: Session already invalidated","messagePattern":"getAttributeNames: Session already invalidated","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"warning","filePath":"weixin-java-common/src/main/java/me/chanjar/weixin/common/session/StandardSession.java","lineNumber":95,"sourceCode":"  @Override\n  public Object getAttribute(String name) {\n\n    if (!isValidInternal()) {\n      throw new IllegalStateException\n        (SM.getString(\"sessionImpl.getAttribute.ise\"));\n    }\n\n    if (name == null) {\n      return null;\n    }\n\n    return this.attributes.get(name);\n  }\n\n  @Override\n  public Enumeration<String> getAttributeNames() {\n    if (!isValidInternal()) {\n      throw new IllegalStateException(SM.getString(\"sessionImpl.getAttributeNames.ise\"));\n    }\n\n    Set<String> names = new HashSet<>();\n    names.addAll(this.attributes.keySet());\n    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;","sourceCodeStart":77,"sourceCodeEnd":113,"githubUrl":"https://github.com/binarywang/WxJava/blob/1c43293a3c2c9d7e91304b6d037fb017f680d0c6/weixin-java-common/src/main/java/me/chanjar/weixin/common/session/StandardSession.java#L77-L113","documentation":"StandardSession.getAttributeNames throws IllegalStateException if the session is no longer valid (invalidated or expired). This mirrors servlet-container session semantics: once isValidInternal() is false, any attribute access is forbidden. The session is used internally by message routers (e.g. WxMpMessageRouter) to track per-user state.","triggerScenarios":"Calling session.getAttributeNames() after session.invalidate() was called, or after the session expired past its maxInactiveInterval.","commonSituations":"A message-router handler retains a WxSession reference and reads attributes after the session timed out; explicit invalidate() followed by a cleanup/access pass; long-lived async work holding a stale session.","solutions":["Call session.isValid() (or the router's validity check) before reading attributes.","Discard session references after invalidate() and re-acquire a fresh session from the manager.","Tune maxInactiveInterval to match your handler's lifetime if timeouts happen mid-flow.","Catch IllegalStateException around post-invalidate cleanup and treat it as 'no attributes'."],"exampleFix":"// before\nEnumeration<String> names = session.getAttributeNames(); // throws if expired\n\n// after\nif (session.isValid()) {\n  Enumeration<String> names = session.getAttributeNames();\n} else {\n  names = Collections.emptyEnumeration();\n}","handlingStrategy":"validation","validationCode":"if (session.isValid()) {\n  Enumeration<String> names = session.getAttributeNames();\n} else {\n  names = Collections.emptyEnumeration();\n}","typeGuard":"private static boolean sessionAccessible(WxSession s) {\n  return s != null && s.isValid();\n}","tryCatchPattern":"try {\n  return session.getAttributeNames();\n} catch (IllegalStateException e) {\n  // session expired/invalidated — treat as empty\n  return Collections.emptyEnumeration();\n}","preventionTips":["Check session.isValid() before any attribute read after a gap.","Do not retain WxSession references across long async work; re-acquire from the manager.","Tune maxInactiveInterval to your handler's expected lifetime."],"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"}