From 2404a0d2726dab95dfe91fa341776095e231420b Mon Sep 17 00:00:00 2001 From: Youichi Fujimoto Date: Mon, 29 Jun 2026 23:02:58 +0900 Subject: [PATCH] fix: return ErrNotFound on doc parse failure instead of empty success MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The godoc/rustdoc/pydoc readers discarded the parser's `matched` flag and cached + returned the (possibly empty) document with a nil error. If a site changed its markup so nothing matched, callers got empty output that looked like "no docs" rather than a failure, and the empty result was cached for the 30-minute TTL — poisoning the cache until expiry. Now Read*Paged and SearchWithin* check the parse result and return ErrNotFound when nothing matched (or the document is blank), and only cache non-empty parses. Applied consistently across godoc, rustdoc, and pydoc. This makes pkg.go.dev/docs.rs/docs.python.org markup drift surface as an error instead of silently empty output (golden tests run against saved HTML and cannot catch live drift). Co-Authored-By: Claude Opus 4.8 --- internal/app/godoc.go | 16 ++++++++++++++-- internal/app/pydoc.go | 16 ++++++++++++++-- internal/app/rustdoc.go | 16 ++++++++++++++-- 3 files changed, 42 insertions(+), 6 deletions(-) diff --git a/internal/app/godoc.go b/internal/app/godoc.go index 2a289c1..c53b4bd 100644 --- a/internal/app/godoc.go +++ b/internal/app/godoc.go @@ -103,7 +103,13 @@ func ReadGoDocPaged( // If no documentation found, try to parse the README section if !matched { - _, parsedDoc = parseReadme(doc) + matched, parsedDoc = parseReadme(doc) + } + + // Nothing parsed: treat as not found instead of caching and returning + // an empty document, which would mask pkg.go.dev markup drift. + if !matched || strings.TrimSpace(parsedDoc) == "" { + return "", 0, false, ErrNotFound } document = parsedDoc @@ -178,7 +184,13 @@ func SearchWithinGoDoc( // If no documentation found, try to parse the README section if !matched { - _, parsedDoc = parseReadme(doc) + matched, parsedDoc = parseReadme(doc) + } + + // Nothing parsed: treat as not found instead of caching and returning + // an empty document, which would mask pkg.go.dev markup drift. + if !matched || strings.TrimSpace(parsedDoc) == "" { + return nil, ErrNotFound } document = parsedDoc diff --git a/internal/app/pydoc.go b/internal/app/pydoc.go index 7655ace..b0d32c5 100644 --- a/internal/app/pydoc.go +++ b/internal/app/pydoc.go @@ -103,7 +103,13 @@ func ReadPyDocPaged( return "", 0, false, errors.Wrap(err, "failed to parse HTML") } - _, document = parsePyDocPage(doc) + matched, parsed := parsePyDocPage(doc) + // Nothing parsed: treat as not found instead of caching and returning + // an empty document, which would mask docs.python.org markup drift. + if !matched || strings.TrimSpace(parsed) == "" { + return "", 0, false, ErrNotFound + } + document = parsed docCache.Set(cacheKey, document, cache.DefaultExpiration) } @@ -161,7 +167,13 @@ func SearchWithinPyDoc( return nil, errors.Wrap(err, "failed to parse HTML") } - _, document = parsePyDocPage(doc) + matched, parsed := parsePyDocPage(doc) + // Nothing parsed: treat as not found instead of caching and returning + // an empty document, which would mask docs.python.org markup drift. + if !matched || strings.TrimSpace(parsed) == "" { + return nil, ErrNotFound + } + document = parsed docCache.Set(cacheKey, document, cache.DefaultExpiration) } diff --git a/internal/app/rustdoc.go b/internal/app/rustdoc.go index 61ae3da..4d66ecf 100644 --- a/internal/app/rustdoc.go +++ b/internal/app/rustdoc.go @@ -71,7 +71,13 @@ func ReadRustDocPaged( return "", 0, false, errors.Wrap(err, "failed to parse HTML") } - _, document = parseDocsRsDocument(doc) + matched, parsed := parseDocsRsDocument(doc) + // Nothing parsed: treat as not found instead of caching and returning + // an empty document, which would mask docs.rs markup drift. + if !matched || strings.TrimSpace(parsed) == "" { + return "", 0, false, ErrNotFound + } + document = parsed docCache.Set(cacheKey, document, cache.DefaultExpiration) } @@ -129,7 +135,13 @@ func SearchWithinRustDoc( return nil, errors.Wrap(err, "failed to parse HTML") } - _, document = parseDocsRsDocument(doc) + matched, parsed := parseDocsRsDocument(doc) + // Nothing parsed: treat as not found instead of caching and returning + // an empty document, which would mask docs.rs markup drift. + if !matched || strings.TrimSpace(parsed) == "" { + return nil, ErrNotFound + } + document = parsed docCache.Set(cacheKey, document, cache.DefaultExpiration) }