elastic/elasticsearch · error · IOException

Truncated server args: expected {} bytes, got {}

Error message

Truncated server args: expected {} bytes, got {}

What it means

Thrown by LaunchDescriptor.readFieldsFrom when DataInputStream.readNBytes(serverArgsBytesLen) returns fewer bytes than the declared length — i.e. the input ended mid-field. This indicates a truncated/cut-off launch descriptor file or stream: the header claimed N bytes of server args but EOF was reached first. IOException (checked).

Source

Thrown at libs/server-launcher-common/src/main/java/org/elasticsearch/server/launcher/common/LaunchDescriptor.java:184

        return readFieldsFrom(in);
    }

    /**
     * Reads descriptor fields without the magic number prefix.
     * Subclasses can call this to read base fields from their own wire format.
     */
    protected static LaunchDescriptor readFieldsFrom(DataInputStream in) throws IOException {
        String command = in.readUTF();
        List<String> jvmOptions = readStringList(in);
        List<String> jvmArgs = readStringList(in);
        Map<String, String> environment = readStringMap(in);
        String workingDir = in.readUTF();
        String tempDir = in.readUTF();
        boolean daemonize = in.readBoolean();
        int serverArgsBytesLen = in.readInt();
        byte[] serverArgsBytes = in.readNBytes(serverArgsBytesLen);
        if (serverArgsBytes.length != serverArgsBytesLen) {
            throw new IOException("Truncated server args: expected " + serverArgsBytesLen + " bytes, got " + serverArgsBytes.length);
        }

        return new LaunchDescriptor(command, jvmOptions, jvmArgs, environment, workingDir, tempDir, daemonize, serverArgsBytes);
    }

    /**
     * Checks the magic number from the stream and throws if it doesn't match.
     */
    protected static void checkMagic(DataInputStream in, int expectedMagic) throws IOException {
        int magic = in.readInt();
        if (magic != expectedMagic) {
            throw new IOException("Invalid launch descriptor: bad magic number");
        }
    }

    /**
     * Returns a human-readable representation of this descriptor in a section-based text format.
     * Useful for debugging with the launcher's --dump flag.

View on GitHub (pinned to db6a809a66)

Solutions

  1. Regenerate the launch descriptor file from the launcher (re-run the launch/bootstrap step) rather than retrying the read.
  2. Ensure the writer flushes and fsyncs the descriptor before signaling the reader, and that the read happens after the write completes.
  3. Verify writer and reader LaunchDescriptor versions match (same field order/length-prefix semantics).
  4. If the length prefix looks implausibly large, treat the descriptor as corrupt and regenerate.
Defensive patterns

Strategy: try-catch

Validate before calling

// Before reading, sanity-check the file size against the declared server args length.
long fileSize = Files.size(path);
// read the length prefix in isolation if exposing a pre-check is feasible.

Try / catch

try {
    return LaunchDescriptor.readFrom(path);
} catch (IOException e) {
    throw new IOException("Launch descriptor at " + path + " is truncated or corrupted; regenerate it", e);
}

Prevention

When it happens

Trigger: Reading a launch-descriptor file that was partially written (the launcher process died mid-write, or disk filled); passing a stream that was closed/severed before all bytes were sent; a length prefix that was corrupted to a huge value so readNBytes hits EOF; reading a file that was overwritten/truncated by a concurrent launcher.

Common situations: Server crash during descriptor serialization leaving a half-written file; inter-process pipe broken before the full payload was flushed; mismatched writer/reader versions where the writer writes fewer trailing bytes than the reader expects.

Related errors


AI-assisted analysis of elastic/elasticsearch@db6a809a66 (2026-08-12). Data as JSON: /api/errors/6dc852f28e4436a7. Report an issue: GitHub.