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
- Read the appended original message and the `cause` to find the real failure inside your transform
- Make the transform defensive: detect file types before parsing and pass unsupported files through unchanged
- 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
- Detect file types before parsing content inside transforms
- Wrap risky parsing in try/catch and throw a clearer domain error
- Test transforms against representative file fixtures including malformed ones
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
- FILE_TRANSFORM_RESULT_INVALID
- hmr must create a channel with a close function
- basePath must be a string
- fingerprint.buildId must be a string
- FILE_NOT_ALLOWED
AI-assisted analysis of remix-run/remix@9696913134 (2026-08-27).
Data as JSON: /api/errors/6efbc60a74bb6d1a.
Report an issue: GitHub.