Skip to content

FastBoot: docs header search box crashes ember serve on routes outside docs #1711

Open

Description

@barryofguilder

Description

When the docs app runs with FastBoot in development, visiting any page outside the docs route throws an autotracking assertion while the page renders. This includes the home page /. The error isn't handled, so it stops the ember serve process.

Error: Assertion Failed: You attempted to update `_records` on `<DocsStoreService:ember203>`, but it had already been used previously in the same computation. Attempting to update a value after using it in a computation can cause logical errors, infinite revalidation bugs, and performance issues, and is not supported.

`_records` was first used:

- While rendering:
  {{outlet}} for -top-level
    -top-level
      {{outlet}} for application
        application
          docs-header
            docs-header/search-box

The update's stack trace goes through DocsStoreService._loadPayload → _fetchProject → findRecord, called from the DocsHeaderSearchBox constructor.

Pages under /docs work. Production builds also prerender fine with prember, likely because assertions are stripped there.

Steps to reproduce

  1. Use the usual setup, where application.hbs renders <DocsHeader /> and the index route has no model hook.
  2. Install ember-cli-fastboot and prember as v11 requires, and run ember serve.
  3. Request / so FastBoot renders it (for example, curl -H 'Accept: text/html' http://localhost:4200/).
  4. The assertion above is thrown and the dev server exits.

Cause

  • DocsHeaderSearchBox's constructor calls this.fetchProject.perform(), which calls docsStore.findRecord('project', …) during render.
  • findRecord reads the tracked _records to check for an existing record.
  • In FastBoot, _fetchProject reads the JSON with fs.readFileSync and has no await before this._loadPayload(payload). So _loadPayload runs synchronously, in the same render that just read _records, and its this._records = { ...this._records } sets the property that was just read.
  • In the browser, the await fetch(…) defers the update, so the problem doesn't show up there.
  • /docs pages avoid it because routes/docs.js loads the project in model() before anything renders.

Workaround

Load the project in the app's application route, as routes/docs.js does:

import Route from '@ember/routing/route';
import { service } from '@ember/service';
import config from 'my-docs-app/config/environment';

export default class ApplicationRoute extends Route {
  @service docsStore;

  model() {
    return this.docsStore.findRecord(
      'project',
      config['ember-cli-addon-docs'].projectName,
    );
  }
}

Possible fixes

  • Defer the store update in the FastBoot branch of _fetchProject, for example with await Promise.resolve() before _loadPayload, so it never happens mid-render.
  • Skip loading the project in the search box's constructor in FastBoot. Search is only interactive in the browser.
  • Load the project in an application-level hook in the addon, so every page has it before the header renders.

Environment

  • ember-cli-addon-docs 11.0.0
  • ember-source 6.7.0
  • ember-cli-fastboot 6.0.0
  • prember 2.1.0
  • ember-concurrency 5.1.0
  • @embroider/compat 3.9.3 and @embroider/webpack 4.1.0
  • Node 22.21.1

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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