denoland/deno · critical · std::io::Error
Unexpected end of data
Error message
Unexpected end of data
What it means
Parsing a deno compile binary's trailing metadata uses length-prefixed byte reads guarded by check_has_len(): every read demands that `len` bytes remain in the input. When the remaining slice is shorter than the declared length, parsing stops with io::ErrorKind::InvalidData 'Unexpected end of data' — the metadata section is truncated relative to its own length fields.
Source
Thrown at cli/lib/standalone/binary.rs:423
let len = u32::from_le_bytes(len_bytes.try_into().unwrap());
Ok((input, len))
}
fn read_u8(input: &[u8]) -> std::io::Result<(&[u8], u8)> {
check_has_len(input, 1)?;
Ok((&input[1..], input[0]))
}
fn read_bytes(input: &[u8], len: usize) -> std::io::Result<(&[u8], &[u8])> {
check_has_len(input, len)?;
let (len_bytes, input) = input.split_at(len);
Ok((input, len_bytes))
}
#[inline(always)]
fn check_has_len(input: &[u8], len: usize) -> std::io::Result<()> {
if input.len() < len {
Err(std::io::Error::new(
std::io::ErrorKind::InvalidData,
"Unexpected end of data",
))
} else {
Ok(())
}
}
View on GitHub (pinned to 9ad36f7a2c)
Solutions
- Rebuild the binary from source with an unchanged toolchain and verify it runs on the build machine before shipping
- Compare file sizes/checksums between build and deploy locations to detect truncation
- Avoid post-processing compiled binaries with packers/signers that rewrite the file layout; Deno's trailer must remain intact
- Ensure the compile process fully succeeds (exit code 0, no disk-full errors) before distributing
Example fix
# before # artifact truncated in transfer deno run ./app # Unexpected end of data # after # rebuild and verify checksum end-to-end deno compile -o app src/mod.ts && sha256sum app scp app host:&& ssh host sha256sum app # must match; then run
Defensive patterns
Strategy: validation
Validate before calling
// Refuse to parse obviously-truncated artifacts
const EXPECTED_MIN = 1024; // whatever your pipeline records at build time
const st = await Deno.stat(binaryPath);
if (st.size < EXPECTED_MIN) throw new Error(`Binary looks truncated: ${st.size} < ${EXPECTED_MIN}`);
const bytes = await Deno.readFile(binaryPath);
if (bytes.at(-1) === undefined || bytes.length === 0) throw new Error("empty binary"); Try / catch
// Rust: parse the standalone trailer defensively
match parse_binary_trailer(&bytes) {
Err(e) if e.kind() == std::io::ErrorKind::InvalidData && e.to_string().contains("Unexpected end of data") => {
// re-download/rebuild; parsing again on the same bytes cannot succeed
Err(anyhow!("compiled binary truncated; expected {} bytes", expected_len))
}
r => r.map_err(Into::into),
} Prevention
- Verify sha256 of compiled binaries at every transfer hop
- Never post-process binaries with tools that rewrite layout (upx, signers) unless Deno documents support
- Ensure compile steps complete with exit 0 before archiving outputs
When it happens
Trigger: A compiled binary truncated by a failed write (disk full, killed process) or partial transfer, so the trailer's declared section lengths exceed the actual bytes; a binary whose bytes were appended-to/repacked with wrong offsets; version-skewed writers emitting lengths a reader interprets differently.
Common situations: `deno compile` output copied with cp/rsync interrupted; docker COPY of a still-being-written file; CI artifact upload truncated; binaries sent over channels that strip trailing bytes (rare) or modified by post-processing tools (upx, signtool) that break Deno's trailer layout.
Related errors
- Unknown media type value: {value}
- Unexpected end of data
- ReadRawBytes() failed
- Unexpected second argument to Deno.bench()
- Unexpected 'fn' field in options, bench function is already
AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20).
Data as JSON: /api/errors/8c067056857ec393.
Report an issue: GitHub.