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 (and

View on GitHub (pinned to e474e8803c)

Solutions

  1. Re-acquire the file entry (re-pick or re-open the file) to get a fresh File handle, then retry.
  2. Clamp byteStart/byteEnd/byteCap to entry.file.size before slicing to avoid out-of-range slices.
  3. 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

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


AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03). Data as JSON: /api/errors/f793977c0da11e9c. Report an issue: GitHub.