remix-run/remix · error · TypeError
Cannot convert LazyFile to string. Use .stream() to get a Re
Error message
Cannot convert LazyFile to string. Use .stream() to get a ReadableStream for Response and other streaming APIs, or .toFile()/.toBlob() for non-streaming APIs that require a complete File/Blob (e.g. FormData). Always prefer .stream() when possible.
What it means
LazyFile intentionally throws a TypeError from its toString() method to prevent implicit string coercion of lazy file content. LazyFile defers loading file data, so converting it to a string would force eager loading and is almost never what streaming code wants. The error directs you to the supported alternatives: .stream() for ReadableStream-based APIs, or .toFile()/.toBlob() when a complete File/Blob is required.
Source
Thrown at packages/lazy-file/src/lib/lazy-file.ts:354
* **Warning:** This reads the entire content into memory, which defeats the purpose of using
* a lazy file for large files. Only use this for non-streaming APIs that require a complete `File`
* (e.g. `FormData`). For streaming, use `.stream()` instead.
*
* @returns A promise that resolves to a native `File`
*/
async toFile(): Promise<File> {
return new File([await this.bytes()], this.name, {
type: this.type,
lastModified: this.lastModified,
})
}
/**
* @throws Always throws a TypeError. LazyFile cannot be implicitly converted to a string.
* Use `.stream()` to get a `ReadableStream` for `Response` and other streaming APIs, or `.toFile()`/`.toBlob()` for non-streaming APIs that require a complete `File`/`Blob` (e.g. `FormData`). Always prefer `.stream()` when possible.
*/
toString(): never {
throw new TypeError(
'Cannot convert LazyFile to string. Use .stream() to get a ReadableStream for Response and other streaming APIs, or .toFile()/.toBlob() for non-streaming APIs that require a complete File/Blob (e.g. FormData). Always prefer .stream() when possible.',
)
}
}
/**
* Union of Blob and lazy blob types.
*/
type BlobLike = Blob | LazyBlob | LazyFile
/**
* Union of BlobPart and lazy blob types. Used for constructor signatures.
*/
type BlobPartLike = BlobPart | LazyBlob | LazyFile
function isBlobLike(value: unknown): value is BlobLike {
return value instanceof Blob || value instanceof LazyBlob || value instanceof LazyFile
}View on GitHub (pinned to 9696913134)
Solutions
- Replace string coercion with file.stream() when feeding Response, fetch bodies, or other streaming APIs
- Use await file.toFile() or await file.toBlob() when the target API needs a complete File/Blob (e.g. FormData)
- If you need a filename or metadata rather than content, read the specific property (e.g. file.name) instead of coercing the whole object
- Audit template literals and String() calls around upload values coming from Remix form data
Example fix
// before
let s = `${lazyFile}` // throws
await fetch(url, { method: 'POST', body: lazyFile })
// after
await fetch(url, { method: 'POST', body: lazyFile.stream() })
// or for FormData:
let form = new FormData()
form.append('file', await lazyFile.toFile(), lazyFile.name) Defensive patterns
Strategy: type-guard
Validate before calling
let isLazyFile = (v: unknown): v is LazyFile =>
typeof v === 'object' && v !== null && 'stream' in v && 'toFile' in v
if (isLazyFile(value)) {
body = value.stream()
} else {
body = String(value)
} Type guard
import type { LazyFile } from 'lazy-file'
function isLazyFile(value: unknown): value is LazyFile {
return (
typeof value === 'object' &&
value !== null &&
typeof (value as LazyFile).stream === 'function' &&
typeof (value as LazyFile).toFile === 'function'
)
} Try / catch
try {
payload = buildBody(value)
} catch (error) {
if (error instanceof TypeError && /Cannot convert LazyFile to string/.test(error.message)) {
payload = value.stream()
} else throw error
} Prevention
- Never template-literal or concatenate values that may be file uploads
- Type upload parameters as LazyFile | string so the compiler forces explicit handling
- Default to .stream() for request bodies; reserve .toFile()/.toBlob() for APIs that require Blob/File
When it happens
Trigger: Any implicit or explicit string conversion of a LazyFile: template literals like `${file}`, string concatenation (file + '...'), String(file), JSON.stringify of an object containing a LazyFile, or passing it to an API that internally coerces to string instead of using .stream()/.toFile()/.toBlob().
Common situations: Using LazyFile upload values with libraries that expect strings (e.g. building a path, encoding into a URL, S3 key construction), logging a form value that is a LazyFile, or serializing a request payload containing a LazyFile to JSON.
AI-assisted analysis of remix-run/remix@9696913134 (2026-08-27).
Data as JSON: /api/errors/2ed6e0ac03a8e7e8.
Report an issue: GitHub.