denoland/deno · critical · std::io::Error

Unexpected end of data

Error message

Unexpected end of data

What it means

The denort runtime that executes `deno compile` binaries parses the embedded data section with length-prefixed reads; check_has_len() enforces that each declared length fits in the remaining bytes. Running short yields io::ErrorKind::InvalidData 'Unexpected end of data' — the binary's embedded section is truncated or its offsets are wrong, so the runtime cannot mount the virtual filesystem/modules.

Source

Thrown at cli/rt/binary.rs:1244

  Ok((input, data))
}

fn read_bytes_with_u32_len(input: &[u8]) -> std::io::Result<(&[u8], &[u8])> {
  let (input, len) = read_u32_as_usize(input)?;
  let (input, data) = read_bytes(input, len)?;
  Ok((input, data))
}

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(())
  }
}

fn read_string_lossy(input: &[u8]) -> std::io::Result<(&[u8], Cow<'_, str>)> {
  let (input, data_bytes) = read_bytes_with_u32_len(input)?;
  Ok((input, String::from_utf8_lossy(data_bytes)))
}

fn read_u32_as_usize(input: &[u8]) -> std::io::Result<(&[u8], usize)> {
  let (input, len_bytes) = read_bytes(input, 4)?;
  let len = u32::from_le_bytes(len_bytes.try_into().unwrap());
  Ok((input, len as usize))
}

View on GitHub (pinned to 9ad36f7a2c)

Solutions

  1. Re-run `deno compile` on the build machine and execute the output there to confirm it works before distribution
  2. Compare byte size and sha256 of the binary at build vs deploy locations; any difference means re-transfer
  3. Free disk space / check CI logs for a failed or partially-written compile step
  4. Do not append to or re-pack compiled binaries; if you must wrap them, ship the original bytes intact inside the wrapper

Example fix

# before
./app  # Unexpected end of data
ls -l app   # 12 MB, but build log says 18 MB -> truncated

# after
deno compile -o app src/mod.ts   # full rebuild
sha256sum app  # verify matches on every hop; then ./app
Defensive patterns

Strategy: validation

Validate before calling

// Validate the compiled binary before first run in deployment scripts
const st = await Deno.stat(appPath);
if (build.size && st.size !== build.size) {
  throw new Error(`${appPath}: ${st.size} bytes on disk, ${build.size} expected — truncated transfer`);
}

Try / catch

const { code, stderr } = await new Deno.Command(appPath).output();
const err = new TextDecoder().decode(stderr);
if (code !== 0 && err.includes("Unexpected end of data")) {
  // binary's embedded section is truncated: re-download from CI, verify sha256, re-run
  throw new Error("compiled binary is truncated; redeploy from a verified artifact");
}

Prevention

When it happens

Trigger: Executing a compiled binary whose data section was cut off (truncated download, interrupted docker layer, disk-full during compile); binaries modified after build (append-oriented tools) that shift the trailer the runtime locates; extreme edge: reading a file that isn't a Deno binary at all through this path.

Common situations: Deploying compile output through artifact stores/proxies that truncate large files; CI caching partial outputs; running a binary built where the compile step silently failed; running on a filesystem with a size limit (FAT32/email attachments) clipping the file.

Related errors


AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20). Data as JSON: /api/errors/fb3348067e6e9774. Report an issue: GitHub.