{"record":{"id":"2ed6e0ac03a8e7e8","repo":"remix-run/remix","slug":"cannot-convert-lazyfile-to-string-use-stream-t","errorCode":null,"errorMessage":"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.","messagePattern":"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\\.","errorType":"validation","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"packages/lazy-file/src/lib/lazy-file.ts","lineNumber":354,"sourceCode":"   * **Warning:** This reads the entire content into memory, which defeats the purpose of using\n   * a lazy file for large files. Only use this for non-streaming APIs that require a complete `File`\n   * (e.g. `FormData`). For streaming, use `.stream()` instead.\n   *\n   * @returns A promise that resolves to a native `File`\n   */\n  async toFile(): Promise<File> {\n    return new File([await this.bytes()], this.name, {\n      type: this.type,\n      lastModified: this.lastModified,\n    })\n  }\n\n  /**\n   * @throws Always throws a TypeError. LazyFile cannot be implicitly converted to a string.\n   * 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.\n   */\n  toString(): never {\n    throw new TypeError(\n      '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.',\n    )\n  }\n}\n\n/**\n * Union of Blob and lazy blob types.\n */\ntype BlobLike = Blob | LazyBlob | LazyFile\n\n/**\n * Union of BlobPart and lazy blob types. Used for constructor signatures.\n */\ntype BlobPartLike = BlobPart | LazyBlob | LazyFile\n\nfunction isBlobLike(value: unknown): value is BlobLike {\n  return value instanceof Blob || value instanceof LazyBlob || value instanceof LazyFile\n}","sourceCodeStart":336,"sourceCodeEnd":372,"githubUrl":"https://github.com/remix-run/remix/blob/9696913134be3a4423513d2775f7b31d6917c049/packages/lazy-file/src/lib/lazy-file.ts#L336-L372","documentation":"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.","triggerScenarios":"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().","commonSituations":"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.","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"],"exampleFix":"// before\nlet s = `${lazyFile}` // throws\nawait fetch(url, { method: 'POST', body: lazyFile })\n\n// after\nawait fetch(url, { method: 'POST', body: lazyFile.stream() })\n// or for FormData:\nlet form = new FormData()\nform.append('file', await lazyFile.toFile(), lazyFile.name)","handlingStrategy":"type-guard","validationCode":"let isLazyFile = (v: unknown): v is LazyFile =>\n  typeof v === 'object' && v !== null && 'stream' in v && 'toFile' in v\n\nif (isLazyFile(value)) {\n  body = value.stream()\n} else {\n  body = String(value)\n}","typeGuard":"import type { LazyFile } from 'lazy-file'\n\nfunction isLazyFile(value: unknown): value is LazyFile {\n  return (\n    typeof value === 'object' &&\n    value !== null &&\n    typeof (value as LazyFile).stream === 'function' &&\n    typeof (value as LazyFile).toFile === 'function'\n  )\n}","tryCatchPattern":"try {\n  payload = buildBody(value)\n} catch (error) {\n  if (error instanceof TypeError && /Cannot convert LazyFile to string/.test(error.message)) {\n    payload = value.stream()\n  } else throw error\n}","preventionTips":["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"],"tags":["lazy-file","typeerror","string-coercion","streams","uploads"],"backgroundTag":"implicit-tostring-conversion","analyzedSha":"9696913134be3a4423513d2775f7b31d6917c049","analyzedAt":"2026-08-27T19:55:01.024Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}