{"record":{"id":"bc3b0c831c81cd68","repo":"fish2018/pansou","slug":"error-bc3b0c","errorCode":null,"errorMessage":"入口脚本未找到搜索路由","messagePattern":"入口脚本未找到搜索路由","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"plugin/xiaokupan/xiaokupan.go","lineNumber":253,"sourceCode":"\n\tassetPath := string(indexAssetPattern.Find(homeBody))\n\tif assetPath == \"\" {\n\t\treturn \"\", fmt.Errorf(\"首页未找到入口脚本\")\n\t}\n\tassetReq, err := http.NewRequestWithContext(ctx, http.MethodGet, strings.TrimRight(p.baseURL, \"/\")+assetPath, nil)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tassetReq.Header.Set(\"User-Agent\", req.Header.Get(\"User-Agent\"))\n\tassetReq.Header.Set(\"Referer\", homeURL)\n\tassetBody, err := doLimitedRequest(client, assetReq, maxDiscoveryBodySize)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"读取入口脚本失败: %w\", err)\n\t}\n\n\trouteIndex := strings.Index(string(assetBody), \"/s/$query\")\n\tif routeIndex < 0 {\n\t\treturn \"\", fmt.Errorf(\"入口脚本未找到搜索路由\")\n\t}\n\twindowStart := max(0, routeIndex-2048)\n\thashes := hashPattern.FindAll(assetBody[windowStart:routeIndex], -1)\n\tif len(hashes) == 0 {\n\t\treturn \"\", fmt.Errorf(\"入口脚本未找到搜索接口标识\")\n\t}\n\treturn string(hashes[len(hashes)-1]), nil\n}\n\nfunc parseSearchResponse(body []byte) ([]model.SearchResult, error) {\n\tvar root serovalNode\n\tif err := stdjson.Unmarshal(body, &root); err != nil {\n\t\treturn nil, fmt.Errorf(\"解析 Seroval 响应失败: %w\", err)\n\t}\n\tdecoder := newSerovalDecoder(&root)\n\tresultNode := decoder.objectValue(&root, \"result\")\n\tsearchResultsNode := decoder.objectValue(resultNode, \"searchResults\")\n\tmergedNode := decoder.objectValue(searchResultsNode, \"merged_by_type\")","sourceCodeStart":235,"sourceCodeEnd":271,"githubUrl":"https://github.com/fish2018/pansou/blob/beaa56133755a548ebc51b090b3816e2ae044aa6/plugin/xiaokupan/xiaokupan.go#L235-L271","documentation":"Once the entry JS bundle is downloaded, the plugin locates the literal client-side search route \"/s/$query\" with strings.Index. If the route string is absent, it cannot derive where the search API identifier is defined and returns this error. The site's JavaScript no longer contains that route literal.","triggerScenarios":"strings.Index(string(assetBody), \"/s/$query\") returns -1: the entry bundle was fetched successfully but does not contain the search route literal — the site changed routing, the wrong script was fetched, or the response is minified/encoded differently.","commonSituations":"Upstream framework upgrade renamed routes (e.g. \"/search?q=\"); the fetched asset is a loader chunk rather than the route-defining chunk; a build changed string encoding (template literals, escaped slashes).","solutions":["Download the asset and grep for the current search route; update the \"/s/$query\" literal in the plugin to the new route string.","If the route moved to another chunk, adjust assetPath extraction (indexAssetPattern) or scan all script chunks until the route is found.","Check whether the string is now escaped in the bundle (e.g. \"\\/s\\/$query\") and normalize assetBody before searching.","Upgrade the plugin to a release matching the current site."],"exampleFix":"// before\nrouteIndex := strings.Index(string(assetBody), \"/s/$query\")\n// after\nnormalized := strings.ReplaceAll(string(assetBody), \"\\\\/\", \"/\")\nrouteIndex := strings.Index(normalized, \"/s/$query\")","handlingStrategy":"fallback","validationCode":"assetBody, err := fetchAsset()\nif err == nil && !strings.Contains(string(assetBody), \"/s/$query\") {\n\tlog.Printf(\"route literal missing from bundle; site likely updated\")\n}","typeGuard":"func hasSearchRoute(body []byte) bool { return strings.Index(string(body), \"/s/$query\") >= 0 }","tryCatchPattern":"funcID, err := discoverServerFunctionID(ctx, req)\nif err != nil && strings.Contains(err.Error(), \"入口脚本未找到搜索路由\") {\n\tlog.Printf(\"xiaokupan route changed: %v\", err)\n\treturn nil, errSkipPlugin\n}","preventionTips":["Monitor for this error as a signal that the upstream site changed its routing.","Search bundles with normalized strings (unescaped slashes) before giving up.","Fall back to scanning all script chunks, not just the first entry asset."],"tags":["scraping","javascript","string-search","upstream-change"],"backgroundTag":"unexpected-response-shape","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"}