Anuken/Mindustry · error · IllegalArgumentException

Data must be exactly 32 bytes (length: ${data.length})

Error message

Data must be exactly 32 bytes (length: ${data.length})

What it means

DataAssetCache.encodeHash encodes a SHA-256 digest (exactly 32 bytes) into a 52-char unpadded base32 string used as the asset cache key. Passing a byte array of any other length is treated as a programming error and rejected immediately.

Source

Thrown at core/src/mindustry/mod/DataAssetCache.java:53

        return hash;
    }

    public boolean has(String hash){
        return get(hash) != null;
    }

    public synchronized @Nullable Fi get(String shaHash){
        if(shaHash == null) return null;
        return hashToFile.get(shaHash);
    }

    public @Nullable Fi get(byte[] shaHash){
        return get(encodeHash(shaHash));
    }

    //base32 without padding; data must be exactly 32 bytes
    public static String encodeHash(byte[] data){
        if(data.length != 32) throw new IllegalArgumentException("Data must be exactly 32 bytes (length: " + data.length + ")");
        char[] out = new char[52];
        int di = 0, oi = 0;

        for(int i = 0; i < 6; i++){
            long bits =
                ((long)(data[di++] & 0xFF) << 32) |
                ((long)(data[di++] & 0xFF) << 24) |
                ((long)(data[di++] & 0xFF) << 16) |
                ((long)(data[di++] & 0xFF) << 8)  |
                ((long)(data[di++] & 0xFF));

            out[oi++] = base32Alphabet[(int)(bits >>> 35) & 0x1F];
            out[oi++] = base32Alphabet[(int)(bits >>> 30) & 0x1F];
            out[oi++] = base32Alphabet[(int)(bits >>> 25) & 0x1F];
            out[oi++] = base32Alphabet[(int)(bits >>> 20) & 0x1F];
            out[oi++] = base32Alphabet[(int)(bits >>> 15) & 0x1F];
            out[oi++] = base32Alphabet[(int)(bits >>> 10) & 0x1F];
            out[oi++] = base32Alphabet[(int)(bits >>> 5) & 0x1F];

View on GitHub (pinned to f695ad7e60)

Solutions

  1. Use SHA-256: MessageDigest.getInstance("SHA-256").digest(...) yields 32 bytes.
  2. Assert data.length == 32 before calling encodeHash.
  3. Do not reuse encodeHash for non-SHA-256 digests.

Example fix

// before
byte[] hash = MessageDigest.getInstance("MD5").digest(bytes);
String key = DataAssetCache.encodeHash(hash); // throws: length 16

// after
byte[] hash = MessageDigest.getInstance("SHA-256").digest(bytes);
String key = DataAssetCache.encodeHash(hash);
Defensive patterns

Strategy: validation

Validate before calling

// Guarantee a 32-byte digest before encoding.
if(data == null || data.length != 32) {
    throw new IllegalArgumentException("SHA-256 hash required (32 bytes), got: " + (data == null ? 0 : data.length));
}
String key = DataAssetCache.encodeHash(data);

Type guard

boolean isValidSha256(byte[] d){ return d != null && d.length == 32; }

Prevention

When it happens

Trigger: encodeHash is called with a hash whose length is not 32 — e.g. MD5 (16), SHA-1 (20), SHA-512 (64), or a truncated/extended buffer.

Common situations: Using the wrong digest algorithm to produce asset hashes; manually constructing hash bytes; corrupted/partial hash from a stream.

Related errors


AI-assisted analysis of Anuken/Mindustry@f695ad7e60 (2026-08-14). Data as JSON: /api/errors/64238e720aafde9f. Report an issue: GitHub.