{"record":{"id":"b1555bda9ba9eae3","repo":"siyuan-note/siyuan","slug":"invalid-publish-service-token","errorCode":null,"errorMessage":"invalid publish service token","messagePattern":"invalid publish service token","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/auth.go","lineNumber":80,"sourceCode":"\n\t// publishSessionTTL 发布服务会话空闲过期时长，超过后需要重新认证\n\tpublishSessionTTL = 7 * 24 * time.Hour\n\t// publishSessionGlobalCap 发布服务会话全局上限，超出后淘汰最久未活跃的会话\n\tpublishSessionGlobalCap = 4096\n\t// publishSessionPerAccountCap 单账户会话上限，超出后淘汰该账户最久未活跃的会话\n\tpublishSessionPerAccountCap = 32\n)\n\nvar (\n\taccountsMap  = AccountsMap{}\n\taccountsLock = sync.RWMutex{}\n\tsessionsMap  = map[string]*PublishSession{}\n\tsessionLock  = sync.Mutex{}\n\n\tjwtKey     = make([]byte, 32)\n\tjwtKeyOnce sync.Once\n\n\tErrInvalidPublishServiceToken = errors.New(\"invalid publish service token\")\n)\n\nfunc InitJwtKey() {\n\tjwtKeyOnce.Do(func() {\n\t\terr := refreshJwtKey()\n\t\tif err != nil {\n\t\t\tlogging.LogFatalf(logging.ExitCodeFatal, \"initialize JWT signing key failed: %s\", err)\n\t\t}\n\t})\n}\n\nfunc refreshJwtKey() error {\n\tif _, err := rand.Read(jwtKey); err != nil {\n\t\tlogging.LogErrorf(\"generate JWT signing key failed: %s\", err)\n\t\treturn err\n\t}\n\treturn nil\n}","sourceCodeStart":62,"sourceCodeEnd":98,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/8641553a1f07374001902d3ce773285db1292b2d/kernel/model/auth.go#L62-L98","documentation":"ErrInvalidPublishServiceToken is the sentinel error returned when a JWT presented as a publish-service token fails validation (IsValidPublishServiceToken returns false during ParseJWT). Publish-service tokens are issued for the publish feature's accounts; once a publish account is (re)initialized the old signing context no longer validates, so tokens signed under the stale account are rejected.","triggerScenarios":"Calling ParseJWT on a token where IsPublishServiceToken(token) is true but IsValidPublishServiceToken fails — e.g. the token was issued for a publish account that was later re-initialized, the token's account credentials no longer match, or the token is malformed/expired relative to the publish account state.","commonSituations":"A publish account was re-created or its password reset, invalidating previously issued JWTs still held by clients; stale tokens cached in a client after the publish service was reconfigured; clock skew or expiry making the publish token invalid.","solutions":["Re-authenticate against the publish service to obtain a fresh token and retry the request","Check whether the publish account was re-initialized; update clients with the new account/token","Verify the token's claims and expiry are intact and match the current publish account configuration"],"exampleFix":"// before\ntoken := cachedPublishToken // issued before account re-init\nclaims, err := ParseJWT(token)\n// after\ntoken := cachedPublishToken\nif _, err := ParseJWT(token); errors.Is(err, ErrInvalidPublishServiceToken) {\n    token = reloginPublishService() // fetch a fresh token\n}\nclaims, err := ParseJWT(token)","handlingStrategy":"try-catch","validationCode":"let valid = true;\ntry { ParseJWT(token); } catch (e) { valid = !errors.Is(e, ErrInvalidPublishServiceToken); }","typeGuard":null,"tryCatchPattern":"claims, err := ParseJWT(token)\nif errors.Is(err, ErrInvalidPublishServiceToken) {\n    token = refreshPublishServiceToken() // re-login and retry once\n    claims, err = ParseJWT(token)\n}","preventionTips":["Refresh publish tokens after any publish account re-initialization or password change","Use errors.Is(err, ErrInvalidPublishServiceToken) to detect stale tokens specifically","Store tokens with their issuing account ID so stale pairs are detected early"],"tags":["jwt","auth","publish-service"],"backgroundTag":"jwt-token-expired","analyzedSha":"8641553a1f07374001902d3ce773285db1292b2d","analyzedAt":"2026-09-11T16:08:28.414Z","contentChangedAt":"2026-09-11T16:08:28.414Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}