{"record":{"id":"b1e7e16695aad8b8","repo":"jackwener/OpenCLI","slug":"zhihu-search-pagination-returned-malformed-next-ur","errorCode":null,"errorMessage":"Zhihu search pagination returned malformed next URL","messagePattern":"Zhihu search pagination returned malformed next URL","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/zhihu/search.js","lineNumber":176,"sourceCode":"        }\n      })()\n    `), url);\n            for (const item of data.data) {\n                const rawType = item?.object?.type;\n                if (type !== 'all' && rawType && rawType !== type) continue;\n                const normalized = normalizeResultItem(item);\n                if (!normalized) continue;\n                if (type !== 'all' && normalized.row.type !== type) continue;\n                if (seen.has(normalized.key)) continue;\n                seen.add(normalized.key);\n                results.push(normalized.row);\n                if (results.length >= resultLimit) break;\n            }\n            if (results.length >= resultLimit) break;\n            if (data.paging?.is_end) break;\n            const next = normalizeSearchUrl(data.paging?.next);\n            if (!next) {\n                throw new CommandExecutionError('Zhihu search pagination returned malformed next URL');\n            }\n            if (visited.has(next)) {\n                throw new CommandExecutionError('Zhihu search pagination returned a repeated next URL');\n            }\n            url = next;\n        }\n        if (results.length === 0) {\n            throw new EmptyResultError('zhihu search', `No ${type === 'all' ? '' : `${type} `}results found for \"${query}\"`);\n        }\n        return results.map((row, i) => {\n            return {\n                rank: i + 1,\n                ...row,\n            };\n        });\n    },\n});\n","sourceCodeStart":158,"sourceCodeEnd":194,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/zhihu/search.js#L158-L194","documentation":"During paginated search, the CLI follows data.paging.next links, normalizing each through normalizeSearchUrl. If the next URL is absent or fails normalization (not a valid/allowed Zhihu search URL), it throws this CommandExecutionError rather than continuing with a broken link. This guards against infinite or invalid pagination states caused by upstream API changes.","triggerScenarios":"Zhihu returns paging.next as null/undefined, an empty string, a relative or non-Zhihu URL, or a URL that normalizeSearchUrl rejects (e.g. wrong host or scheme) while paging.is_end is false and the result limit is not yet reached.","commonSituations":"Zhihu API shape change (paging fields renamed), rate-limiting or login walls returning truncated paging info, proxying the API through a gateway that rewrites the next link.","solutions":["Re-run the search; transient API responses may include proper paging next time","Reduce resultLimit so fewer pages are needed","Log data.paging to inspect the raw next value and adjust normalizeSearchUrl","Handle Zhihu auth/rate limiting so paging metadata is returned intact"],"exampleFix":"// before\nconst next = normalizeSearchUrl(data.paging?.next);\nif (!next) {\n    throw new CommandExecutionError('Zhihu search pagination returned malformed next URL');\n}\n// after\nconst next = normalizeSearchUrl(data.paging?.next);\nif (!next) {\n    if (data.paging?.is_end) break;\n    console.warn('stopping pagination: malformed next URL');\n    break;\n}","handlingStrategy":"validation","validationCode":"function hasNextPage(data) {\n  const next = data?.paging?.next;\n  return typeof next === 'string' && next.startsWith('https://www.zhihu.com/') && next !== '';\n}\nif (data.paging?.is_end !== true && !hasNextPage(data)) throw new Error('unexpected paging state');","typeGuard":"function isValidPagingNext(paging) {\n  return typeof paging?.next === 'string' &&\n    paging.next.length > 0 &&\n    URL.canParse(paging.next) &&\n    new URL(paging.next).hostname.endsWith('zhihu.com');\n}","tryCatchPattern":"try {\n  const results = await zhihuSearch(query, { limit });\n} catch (err) {\n  if (err.message.includes('malformed next URL')) {\n    console.warn('pagination stopped early; returning partial results if available');\n  } else throw err;\n}","preventionTips":["Check paging.is_end before relying on next","Validate paging.next is an absolute https zhihu.com URL before following","Watch for API/auth changes that strip paging metadata","Cap page iterations independently of the API's next links"],"tags":["zhihu","search","pagination","api"],"backgroundTag":"malformed-pagination-url","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}