{"record":{"id":"a58bfed29399d7d2","repo":"fish2018/pansou","slug":"code-403","errorCode":null,"errorMessage":"详情接口返回 code=403，登录状态可能已失效","messagePattern":"详情接口返回 code=403，登录状态可能已失效","errorType":"exception","errorClass":null,"httpStatus":403,"severity":"error","filePath":"plugin/gying/gying.go","lineNumber":2758,"sourceCode":"\t\t\tpreview := string(body)\n\t\t\tif len(preview) > 200 {\n\t\t\t\tpreview = preview[:200] + \"...\"\n\t\t\t}\n\t\t\tfmt.Printf(\"[Gying]     响应内容: %s\\n\", preview)\n\t\t}\n\t\treturn nil, err\n\t}\n\n\tif DebugLog {\n\t\tfmt.Printf(\"[Gying]     详情Code: %d, 网盘链接数: %d\\n\", detail.Code, len(detail.Panlist.URL))\n\t}\n\n\t// 检查JSON响应中的code字段（关键！）\n\tif detail.Code == 403 {\n\t\tif DebugLog {\n\t\t\tfmt.Printf(\"[Gying]     ❌ 详情接口返回Code=403 - 登录状态可能已失效\\n\")\n\t\t}\n\t\treturn nil, fmt.Errorf(\"详情接口返回 code=403，登录状态可能已失效\")\n\t}\n\n\treturn &detail, nil\n}\n\n// buildResult 构建SearchResult\nfunc (p *GyingPlugin) buildResult(detail *DetailData, searchData *SearchData, index int) model.SearchResult {\n\tif index >= len(searchData.L.Title) {\n\t\treturn model.SearchResult{}\n\t}\n\n\ttitle := searchData.L.Title[index]\n\tresourceType := searchData.L.D[index]\n\tresourceID := searchData.L.I[index]\n\n\t// 获取年份并拼接到标题后面\n\tvar year int\n\tif index < len(searchData.L.Year) && searchData.L.Year[index] > 0 {","sourceCodeStart":2740,"sourceCodeEnd":2776,"githubUrl":"https://github.com/fish2018/pansou/blob/beaa56133755a548ebc51b090b3816e2ae044aa6/plugin/gying/gying.go#L2740-L2776","documentation":"fetchDetail parses the JSON DetailData and throws this error when the payload's own code field equals 403 — the site returned well-formed JSON but reports that the login state is invalid. Unlike the HTTP-level 403s, this is an application-layer signal that the session cookie no longer authenticates the account.","triggerScenarios":"Calling fetchDetail when the site returns HTTP 200 JSON whose code field is 403: session expired server-side, cookie revoked by another login, or the account was logged out / banned.","commonSituations":"Long-lived deployments whose cookie TTL elapsed; account logged in elsewhere invalidating the session; site rotating session secrets; using an account that requires re-verification.","solutions":["Re-run the plugin's login flow to refresh the session, then retry.","Persist and reload cookies so restarts don't reuse dead sessions.","If re-login keeps failing, verify the account is valid and not rate-limited/banned in a browser.","Add automatic session-refresh on this error in the calling code."],"exampleFix":"// before\ndetail, err := plugin.fetchDetail(id, typ, scraper) // code=403\n// after\nif err != nil && strings.Contains(err.Error(), \"code=403\") {\n    if rerr := plugin.Relogin(ctx); rerr != nil { return rerr }\n    detail, err = plugin.fetchDetail(id, typ, scraper)\n}","handlingStrategy":"try-catch","validationCode":"// check session health via a lightweight endpoint before batch runs\nif !plugin.SessionHealthy(ctx) { plugin.Relogin(ctx) }","typeGuard":"func isSessionExpired(err error) bool { return err != nil && strings.Contains(err.Error(), \"code=403\") }","tryCatchPattern":"detail, err := plugin.fetchDetail(id, typ, scraper)\nif isSessionExpired(err) {\n    if rerr := plugin.Relogin(ctx); rerr != nil { return rerr }\n    detail, err = plugin.fetchDetail(id, typ, scraper)\n}","preventionTips":["Treat JSON code=403 as \"re-login\" and automate the refresh path.","Persist cookies so restarts don't run on dead sessions.","Avoid logging into the same account from multiple places concurrently.","Monitor session TTL and refresh preemptively."],"tags":["auth","session-expired","api"],"backgroundTag":"authentication-required","analyzedSha":"beaa56133755a548ebc51b090b3816e2ae044aa6","analyzedAt":"2026-09-07T00:31:18.025Z","contentChangedAt":"2026-09-07T00:31:18.025Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}