npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

whisper-ggml-header

v1.0.0

Published

Read and validate the header of a Whisper GGML model. Catches the n_text_ctx=1024 mis-conversion that makes whisper.cpp refuse to load a file. No dependencies.

Readme

whisper-ggml-header

Read and validate the header of a Whisper GGML model. Catches the mis-converted files that whisper.cpp silently refuses to load. Reads 48 bytes. No dependencies.

# Not on npm yet, so npx needs the repository:
npx github:DomenicMoran/whisper-ggml-header model.bin
model.bin
  n_text_ctx    1024   <- wrong
  n_vocab       51865
  n_mels        80
  n_audio_ctx   1500
  n_text_layer  6
  ftype         2008 (q5_0 (qnt v2))
  BLOCKING n_text_ctx: n_text_ctx is 1024, the classic broken conversion.
           The script took max_length from config.json instead of
           max_target_positions. whisper.cpp will not load this file.
  => whisper.cpp will refuse this file

Exit code is 1 when any file would fail to load, so it works as a step in a release pipeline.

The bug it catches

whisper.cpp requires n_text_ctx = 448. Anything else and the model does not load; bindings such as whisper.rn surface that as a generic "model unavailable", with no mention of the header.

A large share of community conversions of fine-tuned Whisper models get this wrong, and the reason is the same every time. The upstream conversion script, models/convert-h5-to-ggml.py, writes max_length from the Hugging Face config.json into the n_text_ctx slot. Several fine-tunes carry:

"max_length": 1024,          // a generation parameter
"max_target_positions": 448  // the actual decoder context length

The right field is max_target_positions. If you are converting yourself, patch the script:

hparams["max_length"] = int(hparams.get("max_target_positions") or 448)
assert hparams["max_length"] == 448, hparams["max_length"]

And check the result before you ship it, which is what this package is for.

Library

import { inspect, parseHeader, checkHeader } from "whisper-ggml-header";
import { open } from "node:fs/promises";

const file = await open("model.bin", "r");
const bytes = new Uint8Array(48);
await file.read(bytes, 0, 48, 0);
await file.close();

const { header, findings, loadable } = inspect(bytes);
if (!loadable) throw new Error(findings[0].message);

Findings are blocking or warning. Blocking means whisper.cpp will refuse the file. Warning means the header is loadable but something does not match a known Whisper configuration, which usually points at a mixed-up tokenizer or mel filter bank.

| Check | Severity | Why | | --- | --- | --- | | magic is 0x67676d6c | blocking | A GGUF file needs a different loader entirely | | n_text_ctx is 448 | blocking | The one whisper.cpp enforces | | n_mels is 80, or 128 for large-v3 | blocking | Mel filter bank does not match the model | | n_vocab is 51864, 51865 or 51866 | warning | Anything else means the tokenizer got mixed up | | n_audio_ctx is 1500 | warning | 30 seconds of audio, the Whisper window | | ftype is a known type | warning | Unknown quantisation |

Header layout

Twelve little-endian int32 values, 48 bytes total:

| Offset | Field | Typical | | --- | --- | --- | | 0 | magic | 0x67676d6c | | 4 | n_vocab | 51865 | | 8 | n_audio_ctx | 1500 | | 12 | n_audio_state | 512 (base) | | 16 | n_audio_head | 8 | | 20 | n_audio_layer | 6 | | 24 | n_text_ctx | 448 | | 28 | n_text_state | 512 | | 32 | n_text_head | 8 | | 36 | n_text_layer | 6 | | 40 | n_mels | 80 | | 44 | ftype | see below |

Reading ftype

Quantised files encode the type as quantisationVersion * 1000 + baseType. describeFtype splits it:

describeFtype(2008); // { quantisationVersion: 2, baseType: 8, label: "q5_0 (qnt v2)" }
describeFtype(1);    // { quantisationVersion: 0, baseType: 1, label: "f16" }

One detail worth reading twice, because it is easy to get backwards: in the ggml_ftype enum 7 is q8_0 and 8 is q5_0, and 5 and 6 do not exist. This was verified against real files rather than assumed: the same base conversion quantised to q5_0 reports 2008 at 55 MB, and to q8_0 reports 2007 at 82 MB.

Where it comes from

An on-device Quran recitation checker in a React Native app. The speech recognition kept reporting itself as unavailable on real devices while everything looked fine in the source tree. The model file had n_text_ctx = 1024, inherited from a third-party conversion whose provenance was not documented.

The fix was to convert the original model in-house with a patched script. The check in this package is what now runs before any model file is published, so the same class of failure cannot reach a device again.

Licence

MIT. See LICENSE.