Skip to content

[Detail Bug] Prerender: response missing statusCode/redirects when both prerender and fetch fail #237

@detail-app

Description

@detail-app

Detail Bug Report

https://app.detail.dev/org_06887db3-bf54-40ab-976d-46c66ab2b840/bugs/bug_4dd09c07-0e25-4b81-bc39-ec50c4055499

Summary

  • Context: The prerender function in src/index.js attempts to render a webpage using a headless browser (puppeteer/browserless), while simultaneously fetching the page with a simple HTTP request as a fallback mechanism.
  • Bug: When both prerender and fetch operations fail, the returned response object is missing the statusCode and redirects fields that are present in all other code paths.
  • Actual vs. expected: The function returns { headers, html, url, mode } instead of { headers, html, url, mode, statusCode, redirects }, creating an inconsistent API surface.
  • Impact: Consumer code that expects statusCode to always be present (e.g., for error handling like if (result.statusCode >= 400)) will receive undefined, potentially causing runtime errors or incorrect behavior.

Code with Bug

return isFetchResRejected
  ? {
      headers: data.headers || {},
      html: '',
      url,
      mode: 'prerender'
      // <-- BUG 🔴 missing `statusCode` and `redirects` fields in this failure path
    }
  : data

Explanation

In prerender, browserless rendering and a parallel fetch fallback run together. If browserless fails, the function falls back to the fetch result. However, when the fetch result is also rejected (reflect: true case), the fallback return constructs a partial object and omits statusCode and redirects.

This is inconsistent with:

  • fetch error returns, which include statusCode and redirects
  • success returns from both fetch and prerender, which include statusCode (and redirects in newer code)

Codebase Inconsistency

fetch error handling (non-reflect) always includes these fields:

return reflect
  ? { isRejected: true, error }
  : {
      url,
      html: '',
      mode: 'fetch',
      headers: error.response ? error.response.headers : {},
      statusCode: error.response ? error.response.statusCode : undefined,
      redirects
    }

Existing tests also assume statusCode is always present:

t.is(prerenderDisabled.statusCode, prerenderEnabled.statusCode)
t.is(prerenderDisabled.statusCode, 200)

Recommended Fix

Include statusCode and redirects in the isFetchResRejected fallback return so the return shape is consistent:

return isFetchResRejected
  ? {
      headers: data.headers || {},
      html: '',
      url,
      mode: 'prerender',
      statusCode: data.error?.response?.statusCode,
      redirects: []
    }
  : data

History

This bug was introduced in commit b66c1e0. The bug was later compounded in commit 54cfe61 when the redirects field was added to all paths except this same overlooked error fallback.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions