windmill-labs/windmill · error · FileReadError
${e instanceof Error ? e.message : String(e)}
Error message
${e instanceof Error ? e.message : String(e)} What it means
FileReadError wraps any failure raised by Blob.slice(...).text() when the fileEngine reads a portion of a chat file entry. The original exception message is embedded ('Invalid AI agent provider configuration' style: 'FileReadError: <name>: <reason>'), so the underlying cause (decode failure, file handle gone, quota) is preserved in e.message.
Source
Thrown at frontend/src/lib/components/copilot/chat/files/fileEngine.ts:138
let end = requestedEnd
if (end < start) end = start
const cappedByLines = end - start + 1 > maxLines
if (cappedByLines) end = start + maxLines - 1
if (end > totalLines) end = totalLines
const byteStart = entry.lineIndex[start - 1]
const byteEnd = end < totalLines ? entry.lineIndex[end] : entry.file.size
// Bound the decode for newline-sparse files (minified JS, single-line JSONL): the
// window can span the whole file, but we only ever return maxChars characters, and
// a UTF-8 character is at most 4 bytes — so never materialize more than that.
const byteCap = byteStart + maxChars * 4
const byteCapped = byteCap < byteEnd
let text: string
try {
text = await entry.file.slice(byteStart, byteCapped ? byteCap : byteEnd).text()
} catch (e) {
throw new FileReadError(entry.name, e instanceof Error ? e.message : String(e))
}
let cappedByChars = byteCapped
if (text.length > maxChars) {
text = text.slice(0, maxChars)
cappedByChars = true
}
// When the char cap truncates the window short of `end`, the text holds fewer lines
// than requested — so the note must report the last line actually returned and resume
// at the next unread one (otherwise it claims lines it didn't return and skips them).
let lastLine = end
let resumeAt: number | undefined = end < totalLines ? end + 1 : undefined
if (cappedByChars) {
const completeLines = (text.match(/\n/g) || []).length
if (completeLines >= 1) {
// lines start..start+completeLines-1 are whole; the next line was cut mid-content.
// Trim that partial line off the returned text so the body matches the note (andView on GitHub (pinned to e474e8803c)
Solutions
- Re-acquire the file entry (re-pick or re-open the file) to get a fresh File handle, then retry.
- Clamp byteStart/byteEnd/byteCap to entry.file.size before slicing to avoid out-of-range slices.
- Catch FileReadError and fall back to asking the user to re-upload or re-attach the file.
Example fix
// before
const text = await entry.file.slice(start, end).text()
// after
let text
try {
text = await entry.file.slice(Math.min(start, entry.file.size), Math.min(end, entry.file.size)).text()
} catch (e) {
throw new FileReadError(entry.name, e instanceof Error ? e.message : String(e))
} Defensive patterns
Strategy: try-catch
Validate before calling
if (entry.file.size === 0) throw new FileReadError(entry.name, 'file is empty') if (byteEnd > entry.file.size) byteEnd = entry.file.size
Type guard
function isReadableFileEntry(entry: unknown): entry is { name: string; file: File } {
return typeof entry === 'object' && entry !== null &&
'file' in entry && entry.file instanceof File && entry.file.size > 0
} Try / catch
try {
text = await entry.file.slice(start, end).text()
} catch (e) {
if (e instanceof FileReadError) {
return reAcquireAndRetry(entry.name)
}
throw e
} Prevention
- Clamp slice ranges to the blob size before reading.
- Re-validate that the File handle is still live (size > 0) before reading.
- Fall back to re-acquiring the file from the user when a read fails.
When it happens
Trigger: entry.file.slice(byteStart, byteCapped ? byteCap : byteEnd).text() rejects — e.g. the underlying File/Blob handle is stale after the source file changed on disk, the slice range is invalid, or the browser fails to read the bytes.
Common situations: Reading a file whose backing File object came from a File System Access API handle that was revoked; a truncated or locked file; browser storage eviction invalidating the handle; slicing beyond blob size.
Understand the failure class
Background: "failed to read file", EACCES, ENOENT and "could not read <path>" errors: when a program can't read a file from disk — this error's family across 49 libraries.
Related errors
- File not found: ${filePath}
- Workspace folder not found, are you in the right directory?
- Directory not found: ${targetDirectory}
- Path is not a directory: ${targetDirectory}
- file path must refer to a file.
AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03).
Data as JSON: /api/errors/f793977c0da11e9c.
Report an issue: GitHub.