can1357/oh-my-pi · error · ArchiveError

${error instanceof Error ? error.message : String(error)}

Error message

${error instanceof Error ? error.message : String(error)}

What it means

zstdDecompress wraps any failure from the underlying async zstd decoder into an ArchiveError carrying the native decoder's message. This is the surface for all zstd frame problems: corrupt frames, magic mismatch, exceeding maxOutputLength, or unsupported frame parameters.

Source

Thrown at packages/utils/src/ar/codecs/zstd.ts:18

import { promisify } from "node:util";
import * as zlib from "node:zlib";
import { ArchiveError } from "../error";

const zstdCompressAsync = promisify(zlib.zstdCompress);
const zstdDecompressAsync = promisify(zlib.zstdDecompress);

/** zstd frame magic: 28 b5 2f fd. */
export function isZstd(bytes: Uint8Array): boolean {
	return bytes.byteLength >= 4 && bytes[0] === 0x28 && bytes[1] === 0xb5 && bytes[2] === 0x2f && bytes[3] === 0xfd;
}

/** Decompress one zstd frame, bounded to `maxOutput` bytes. */
export async function zstdDecompress(bytes: Uint8Array, maxOutput: number): Promise<Uint8Array> {
	try {
		return await zstdDecompressAsync(bytes, { maxOutputLength: Math.max(maxOutput, 1) });
	} catch (error) {
		throw new ArchiveError(error instanceof Error ? error.message : String(error));
	}
}

/** Compress bytes as one zstd frame (tar.zst writing). */
export function zstdCompress(bytes: Uint8Array): Promise<Uint8Array> {
	return zstdCompressAsync(bytes);
}

View on GitHub (pinned to 9690622007)

Solutions

  1. Verify the input starts with the zstd magic (0x28 B5 2F FD) before calling
  2. Raise maxOutput if the native message indicates the output limit was hit and the input is trusted
  3. Re-download/re-extract the file if the message indicates corruption
  4. Ensure you are using the right codec for the file (zstd vs gzip vs xz)

Example fix

// before
await zstdDecompress(bytes, 10 * 1024 * 1024); // bombs out on large frames
// after
if (!(bytes[0] === 0x28 && bytes[1] === 0xb5 && bytes[2] === 0x2f && bytes[3] === 0xfd)) throw new Error('not zstd');
await zstdDecompress(bytes, 256 * 1024 * 1024);
Defensive patterns

Strategy: validation

Validate before calling

function isZstdFrame(b: Uint8Array): boolean {
  return b.byteLength >= 4 && b[0] === 0x28 && b[1] === 0xb5 && b[2] === 0x2f && b[3] === 0xfd;
}
if (!isZstdFrame(bytes)) throw new Error('not a zstd frame');

Type guard

function isZstdFrame(b: Uint8Array): boolean {
  return b.byteLength >= 4 && b[0] === 0x28 && b[1] === 0xb5 && b[2] === 0x2f && b[3] === 0xfd;
}

Try / catch

try {
  return await zstdDecompress(bytes, maxOutput);
} catch (err) {
  if (err instanceof ArchiveError) {
    if (/output/i.test(err.message)) return null; // hit maxOutput: bomb or limit too low
    return null; // corrupt/invalid frame
  }
  throw err;
}

Prevention

When it happens

Trigger: Calling zstdDecompress on bytes that are not a valid zstd frame; a frame whose decompressed size exceeds maxOutputLength; truncated .tar.zst or .zst payloads.

Common situations: Corrupt uploads; decompression-bomb protection tripping maxOutputLength; files with wrong extension decompressed with the wrong codec; zstd frames produced with dictionaries or parameters the decoder rejects.

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/660fa20416e3ca53. Report an issue: GitHub.