Repository navigation
fix(content): degrade missing-BLOB race to styled page, not raw 500 (#3103) - #3104
Merged
Merged
Conversation
…3103) Content page loads (/concepts/:slug, /tutorials/:slug) intermittently returned a raw 500 "Internal Server Error". Root cause: serveStoredSlug reads BLOB metadata and the BLOB in two separate queries (to avoid the HANA LOB-locator-expiry gotcha). When metadata hits but the separate BLOB read returns nothing — the inconsistent-read window during a concurrent publish/delta-write to the mutable ContentCurrent, or a transient HANA locator blip — blobRow.CONTENT / toBuffer(null) threw, and serveHandler's catch surfaced raw JSON instead of the styled page. - serveStoredSlug: guard null/missing/non-Buffer/zero-length BLOB → return 'not-found' (styled 404); emit content.blob.missing metric. Both HANA and SQLite branches now coerce via toBuffer + share the guard. - serveHandler catch: serveNotFound(res, slug, 500) (styled page, 500 kept for monitoring) instead of raw JSON — mirrors the catalog branch and the fail-open pages path. - serveNotFound: no-store on 5xx so a transient blip served as a styled 500 is never edge-cached for 60s (404s stay cacheable). - resolveContentBlob: same toBuffer(null) guard in both branches, so the error-page/nav resolver can't throw on the identical race. Covers srv and srv-qa (both shim over @tutorials/content). Test: publish a slug, null its content column (metadata intact), assert styled 404 not 500. Red before, green after.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #3103.
Problem
Intermittent raw HTTP 500 "Internal Server Error" on content page loads (e.g.
/concepts/ui5-supportrc-file/,/tutorials/:slug). Found while debugging live PROD 500s on 2026-11-11.Two compounding defects:
serveStoredSlugreads BLOB metadata and the BLOB itself in two separate queries (to avoid the HANA LOB-locator-expiry gotcha). When metadata hits but the separate BLOB read returns nothing — the inconsistent-read window during a concurrent publish/delta-write to the mutableContentCurrent(overwrite-in-place, no version pin), or a transient HANA locator blip —blobRow.CONTENT/toBuffer(null)threw.serveHandler's catch surfaced raw JSON{"error":"Content retrieval failed"}instead of the styled page. The siblingpageServeHandler/authorServeHandler/advocateServeHandlerall fail open — the tutorial/concept path was the outlier. No global Express error middleware backstops it.Net: a transient, self-resolving infra condition showed users a bare "Internal Server Error."
Fix
serveStoredSlug— guard null/missing/non-Buffer/zero-length BLOB → return'not-found'(styled 404); emitcontent.blob.missingmetric. Both HANA and SQLite branches now coerce viatoBufferand share theBuffer.isBufferguard.serveHandlercatch —serveNotFound(res, slug, 500)(styled page, 500 kept for monitoring) instead of raw JSON. Mirrors the catalog branch.serveNotFound—Cache-Control: no-storeon 5xx so a transient blip served as a styled 500 is never edge-cached for 60s (404s stay cacheable).resolveContentBlob— sametoBuffer(null)guard in both branches (the error-page/nav resolver had the identical latent bug).Covers
srvandsrv-qa(both shim over@tutorials/content).Test
test/lib/content-store.test.js: publish a slug, null itscontentcolumn (metadata intact), assert styled 404 not 500. Red before, green after. Full content-store suite 56/56; render-cache + catalog-slugs suites green (62/62 total).Not in scope
The admin/API JSON endpoints (
/content/hashes,/content/nav, publish, rollback, markdown) keep raw-JSON 500s — those are machine/admin consumers, not browser page loads. Related error-log noise (cds-caching unique-constraint) filed separately as #3102.🤖 Generated with Claude Code