janhq/jan · error

Invalid GGUF file(s)

Error message

Invalid GGUF file(s): ${error.message || 'File format validation failed'}

What it means

During load, the extension validates the GGUF file(s) (including reading metadata such as nextn/draft layer counts). Any exception from that validation is logged and rethrown as 'Invalid GGUF file(s): ...', meaning the file is corrupt, truncated, or not a GGUF at all.

Solutions

  1. Delete the corrupt file and re-download the model; verify size/checksum against the source
  2. Confirm the file is actually GGUF format (magic bytes 'GGUF'), not safetensors or an HTML error page
  3. Re-import the model pointing at a known-good GGUF build
  4. Check available disk space and retry the download if it was truncated

Example fix

// before
await loadModel({ modelPath: '/models/model.gguf' }) // truncated download
// after
await downloadModel('https://.../model.gguf', { overwrite: true }) // re-fetch
await loadModel({ modelPath: '/models/model.gguf' })
Defensive patterns

Strategy: validation

Validate before calling

const fd = await fs.openAsync(path, 'r'); const buf = Buffer.alloc(4)
await fd.read(buf, 0, 4, 0)
if (buf.toString('ascii', 0, 4) !== 'GGUF') throw new Error(`not a GGUF: ${path}`)
await loadModel({ modelPath: path })

Try / catch

try {
  await loadModel(opts)
} catch (e) {
  if (e.message.startsWith('Invalid GGUF file(s):')) {
    await redownloadCorruptedModel(opts.modelPath); return loadModel(opts)
  }
  throw e
}

Prevention

When it happens

Trigger: Calling loadModel with a modelPath pointing to a corrupt or truncated .gguf download, a non-GGUF file (e.g. safetensors, HTML error page saved as .gguf), or an mmproj file that fails format checks.

Common situations: Interrupted downloads leaving partial GGUFs; wrong file picked manually (safetensors from HuggingFace instead of GGUF); proxy/captive portal returning HTML that got saved as the model; corrupted storage.

Related errors


AI-assisted analysis of janhq/jan@7205d770c1 (2026-09-17). Data as JSON: /api/errors/81d31802a03f1006. Report an issue: GitHub.

Appendix: source

Thrown at extensions/llamacpp-extension/src/index.ts:2092

        // Only asked for a DFlash-architecture draft, since that is the one
        // case a tensor decides (DSpark = DFlash + Markov head).
        const hasMarkovHead =
          draftMetadata.metadata?.['general.architecture'] === 'dflash'
            ? (await findGgufTensors(fullDraftPath, [MARKOV_HEAD_TENSOR]))
                .length > 0
            : undefined
        specKind = resolveSpecDraftKind(draftMetadata.metadata, {
          hasMarkovHead,
          hint: opts.specDraftKind,
        })
        const draftLayers = detectMtpLayersFromGgufMeta(draftMetadata.metadata)
        // Only an MTP head reports nextn layers; the other flavours have none,
        // and the count is what makes the settings panel offer the toggle.
        mtpLayers = draftLayers > 0 ? draftLayers : Math.max(mtpLayers, 1)
      }
    } catch (error) {
      logger.error('GGUF validation failed:', error)
      throw new Error(
        `Invalid GGUF file(s): ${
          error.message || 'File format validation failed'
        }`
      )
    }

    // Calculate file sizes
    let size_bytes = (await fs.fileStat(fullModelPath)).size
    if (mmprojPath) {
      size_bytes += (
        await fs.fileStat(await joinPath([janDataFolderPath, mmprojPath]))
      ).size
    }
    if (draftModelPath) {
      size_bytes += (
        await fs.fileStat(await joinPath([janDataFolderPath, draftModelPath]))
      ).size
    }

View on GitHub (pinned to 7205d770c1)