remix-run/remix · error · AssetServerCompilationError

FILE_TRANSFORM_FAILED

FILE_TRANSFORM_FAILED

Error message

${formatFileTransformSubject(transformSubject)} failed for ${filePath}. ${formatUnknownError(error)}

What it means

This is the wrapper that runs user-supplied file transforms (per-file or global): if the transform throws or its promise rejects, the error is rewrapped into an AssetServerCompilationError with code FILE_TRANSFORM_FAILED, preserving the original as `cause` and appending its formatted message. Non-compilation errors from inside the transform always surface this way.

Source

Thrown at packages/assets/src/lib/files/compiler.ts:776

      { code: 'FILE_TRANSFORM_RESULT_INVALID' },
    )
  }

  return normalizedExtension
}

function toFileTransformFailedError(
  error: unknown,
  filePath: string,
  transformSubject: FileTransformSubject,
): AssetServerCompilationError {
  if (isAssetServerCompilationError(error)) return error

  return createAssetServerCompilationError(
    `${formatFileTransformSubject(transformSubject)} failed for ${filePath}. ${formatUnknownError(error)}`,
    {
      cause: error,
      code: 'FILE_TRANSFORM_FAILED',
    },
  )
}

function getGlobalFileTransformSubject(
  globalTransform: FileCompilerOptions['globalTransforms'][number],
  index: number,
): FileTransformSubject {
  let functionName = globalTransform.transform.name
  return {
    index,
    kind: 'global',
    name: globalTransform.name ?? (functionName.length > 0 ? functionName : undefined),
  }
}

function formatFileTransformSubject(transformSubject: FileTransformSubject): string {
  if (transformSubject.kind === 'request') {

View on GitHub (pinned to 9696913134)

Solutions

  1. Read the appended original message and the `cause` to find the real failure inside your transform
  2. Make the transform defensive: detect file types before parsing and pass unsupported files through unchanged
  3. Wrap risky parsing in try/catch inside the transform and either return original content or throw a clearer domain error

Example fix

// before
transform: (content) => JSON.parse(content) // throws SyntaxError on non-JSON → FILE_TRANSFORM_FAILED

// after
transform: (content, filePath) => {
  if (!filePath.endsWith('.json')) return content
  try {
    return JSON.stringify(JSON.parse(content))
  } catch (error) {
    throw new Error(`Invalid JSON in ${filePath}: ${error.message}`)
  }
}
Defensive patterns

Strategy: try-catch

Type guard

function isAssetServerCompilationError(error: unknown): error is { code: string; cause?: unknown } {
  return !!error && typeof error === 'object' && 'code' in error && (error as any).code === 'FILE_TRANSFORM_FAILED'
}

Try / catch

try {
  await compileFiles(files, { transforms: [myTransform] })
} catch (error) {
  if (isAssetServerCompilationError(error)) {
    console.error(`transform failed: ${error.cause?.message ?? error.message}`)
    // fail the build with the underlying cause, or skip the file
  }
  throw error
}

Prevention

When it happens

Trigger: Any exception inside a transform callback: JSON.parse failing on file content, fs read of a helper file missing, a TypeError from bad string manipulation, or a rejected async transform. The original error's message appears after 'failed for <filePath>'.

Common situations: Transforms assuming a specific file format (parsing CSS as JSON); missing optional dependencies in the transform's environment; file encoding surprises (binary file passed as string); runtime differences between dev and build environments.

Related errors


AI-assisted analysis of remix-run/remix@9696913134 (2026-08-27). Data as JSON: /api/errors/6efbc60a74bb6d1a. Report an issue: GitHub.