facebook/react · error · Error

Expected the transformed source to be a string.

Error message

Expected the transformed source to be a string.

What it means

While handling `export * from ...` declarations, the RSC ESM loader recursively loads the re-exported module through the loader chain and expects result.source to be a string before parsing it with acorn to enumerate export names. Node's load hooks may legitimately return ArrayBuffer sources under newer protocol versions, and chained loaders can return other shapes; anything non-string hits this throw.

Source

Thrown at packages/react-server-dom-esm/src/ReactFlightESMNodeLoader.js:530

  parentURL: string,
  loader: LoadFunction,
): Promise<void> {
  for (let i = 0; i < body.length; i++) {
    const node = body[i];
    switch (node.type) {
      case 'ExportAllDeclaration':
        if (node.exported) {
          addExportNames(names, node.exported);
          continue;
        } else {
          const {url} = await resolveClientImport(node.source.value, parentURL);
          const {source} = await loader(
            url,
            {format: 'module', conditions: [], importAssertions: {}},
            loader,
          );
          if (typeof source !== 'string') {
            throw new Error('Expected the transformed source to be a string.');
          }
          let childBody;
          try {
            childBody = acorn.parse(source, {
              ecmaVersion: '2024',
              sourceType: 'module',
            }).body;
          } catch (x) {
            // eslint-disable-next-line react-internal/no-production-logging
            console.error('Error parsing %s %s', url, x.message);
            continue;
          }
          await parseExportNamesInto(childBody, names, url, loader);
          continue;
        }
      case 'ExportDefaultDeclaration':
        names.push('default');
        continue;

View on GitHub (pinned to eafeac097b)

Solutions

  1. Replace `export * from './client-mod'` with explicit named re-exports so the loader never needs to load and parse the child module
  2. Ensure every loader in the chain normalizes source to a string (convert ArrayBuffer with new TextDecoder().decode(source) before returning)
  3. Match react-server-dom-esm to a Node version its loader supports; check the package release notes for loader fixes

Example fix

// before (barrel re-export in a client boundary)
export * from './buttons';

// after (explicit names)
export {Button, LinkButton} from './buttons';
Defensive patterns

Strategy: validation

Validate before calling

// for loader-chain authors: normalize before returning
async function load(url, context, nextLoad) {
  const result = await nextLoad(url, context);
  if (result.source != null && typeof result.source !== 'string') {
    result.source = new TextDecoder().decode(result.source);
  }
  return result;
}

Prevention

When it happens

Trigger: A client-reference module that uses `export * from './other'` where the chained loader (or Node's default load for certain URL schemes/versions) returns an ArrayBuffer or null source; loaders like tsx/esbuild-register in the chain returning non-string sources; Node version drift in the load-hook contract under this package's supported matrix.

Common situations: Monorepos whose server graphs include barrel files re-exporting client components; combining the RSC loader with TypeScript loaders on newer Node; data: URLs or exotic loaders feeding non-string sources into the chain.

Related errors


AI-assisted analysis of facebook/react@eafeac097b (2026-08-21). Data as JSON: /api/errors/928d7829ba8ebec3. Report an issue: GitHub.