remix-run/remix · error · AssetServerCompilationError
FILE_TRANSFORM_RESULT_INVALID
FILE_TRANSFORM_RESULT_INVALID
Error message
${formatFileTransformSubject(options.transformSubject)} must return a string, Uint8Array, or object for ${options.filePath} What it means
A file transform (per-file or global) must return transformed content as a string, a Uint8Array, or a result object. After awaiting the transform, the compiler rejects any return value that is null or not an object when it expected the object form — i.e. the transform returned an unusable type such as a number, boolean, null, or undefined, failing with FILE_TRANSFORM_RESULT_INVALID.
Source
Thrown at packages/assets/src/lib/files/compiler.ts:710
): AssetFileTransformResult & { content: Uint8Array; extension: string } {
if (typeof result === 'string') {
return {
content: new TextEncoder().encode(result),
extension: options.currentExtension,
}
}
if (result instanceof Uint8Array) {
return {
content: result,
extension: options.currentExtension,
}
}
if (result === null || typeof result !== 'object') {
throw createAssetServerCompilationError(
`${formatFileTransformSubject(options.transformSubject)} must return a string, Uint8Array, or object for ${options.filePath}`,
{ code: 'FILE_TRANSFORM_RESULT_INVALID' },
)
}
if (
!('content' in result) ||
(typeof result.content !== 'string' && !(result.content instanceof Uint8Array))
) {
throw createAssetServerCompilationError(
`${formatFileTransformSubject(options.transformSubject)} must return a string or Uint8Array content value for ${options.filePath}`,
{ code: 'FILE_TRANSFORM_RESULT_INVALID' },
)
}
let extension = options.currentExtension
if ('extension' in result && result.extension !== undefined) {
if (typeof result.extension !== 'string') {
throw createAssetServerCompilationError(
`${formatFileTransformSubject(options.transformSubject)} must return a string extension for ${options.filePath}`,View on GitHub (pinned to 9696913134)
Solutions
- Return the content explicitly: a string, a Uint8Array, or `{ content, extension? }`
- For 'no change' paths, return the input content (or its bytes) rather than undefined/null
- Add a unit test asserting every branch of the transform returns a value
Example fix
// before
transform: (content, filePath) => {
if (!filePath.endsWith('.md')) return // → undefined
return minify(content)
}
// after
transform: (content, filePath) => {
if (!filePath.endsWith('.md')) return content
return minify(content)
} Defensive patterns
Strategy: validation
Validate before calling
function validateTransformResult(result: unknown): string | Uint8Array | { content: string | Uint8Array; extension?: string } {
if (typeof result === 'string' || result instanceof Uint8Array) return result
if (result && typeof result === 'object' && 'content' in result) return result as any
throw new TypeError('transform must return string, Uint8Array, or { content, extension? }')
}
// wrap your transform:
const safeTransform = (content, filePath) => validateTransformResult(myTransform(content, filePath)) Type guard
function isValidTransformResult(result: unknown): boolean {
return (
typeof result === 'string' ||
result instanceof Uint8Array ||
(!!result && typeof result === 'object')
)
} Prevention
- Never let a transform branch return undefined — return original content instead
- Unit-test every branch of custom transforms
When it happens
Trigger: A transform function that returns undefined (falls off the end), returns null, or returns a primitive like a number; also returning a Buffer-like or stream object that isn't a Uint8Array or the expected object shape.
Common situations: Writing a transform with an early-return path that forgets to return content; returning `undefined` to mean 'no change' (this API does not support that — return the original content instead); async transforms whose promise resolves to nothing because of a missed return.
Related errors
- hmr must create a channel with a close function
- basePath must be a string
- fingerprint.buildId must be a string
- files.extensions must be an array
- files.extensions values must be strings
AI-assisted analysis of remix-run/remix@9696913134 (2026-08-27).
Data as JSON: /api/errors/652aec684dfbf94d.
Report an issue: GitHub.