Skip to content

fix(transcription): give the model-registry JSON import its type attribute - #1933

Merged
gabrielste1n merged 2 commits into
mainfrom
fix/policy-rules-json-import-attribute
Aug 29, 2026
Merged

fix(transcription): give the model-registry JSON import its type attribute#1933
gabrielste1n merged 2 commits into
mainfrom
fix/policy-rules-json-import-attribute

Conversation

@gabrielste1n

Copy link
Copy Markdown
Collaborator

Three transcription paths have thrown needs an import attribute of "type: json" in packaged builds since 1.9.1, on every platform. One missing import attribute. Also prepares 1.9.2.

Fixes #1908

Root cause

policyRules.ts gained import modelRegistryData from "../models/modelRegistryData.json" in 1.9.1 via #1836. ipcHandlers.js reaches that module through Electron's bare Node ESM loader, which requires with { type: "json" } on JSON imports. Reproducible straight from the source tree, no packaging needed:

$ node -e 'import("./src/stores/policyRules.ts")'
ERR_IMPORT_ATTRIBUTE_MISSING: Module ".../src/models/modelRegistryData.json"
needs an import attribute of "type: json"

voiceSurfaceGeometry.mjs:1 already writes it the correct way, so the fix matches what is already in the tree.

Blast radius

I walked the main-process ESM graph: 7 modules, exactly one bad JSON import. ipcHandlers.js is the only main-process file that imports raw .ts, and it does so from three handlers:

Handler Feature
proxy-xai-transcription xAI dictation
retry-transcription Re-transcribing a saved recording
transcribe-audio-file-byok Bring-your-own-key file uploads

Ordinary dictation is unaffected, which matches the reports.

Narrower than the issue thread suggests. audioManager.js and discardedRecording.js are renderer modules (imported by ControlPanel.tsx and useAudioRecording.js), so Vite inlines their JSON. languageSupport.ts, prompts.ts and translations.ts are not in the main-process graph either — i18nMain.js bypasses prompts.ts and require()s the raw per-locale JSON. ModelRegistry.ts carries the same shape but every importer uses an extensionless specifier, which Node's resolver does not resolve, so it is latent rather than reachable. Only the one site is fixed here.

Why the existing tests passed

test/helpers/transcriptionRoute.test.js already imports the exact module that fails in production, 13 tests deep. The suite runs under node --import tsx, and tsx resolves attribute-less JSON imports:

node --import tsx --test  →  13 pass     # what CI runs
node --test               →  13 fail     # what the packaged app does

So adding another ordinary test would not have helped. The new test spawns each main-process ESM entry in a child process with NODE_OPTIONS stripped, so it exercises the loader the packaged app actually uses. Verified red-first: it fails without the one-line fix and passes with it.

eslint.config.js:8 also ignores helpers/** and utils/**, so no static rule could have caught this either.

1.9.2

Bumps the version and moves the changelog's Unreleased entries into a [1.9.2] section covering everything since 1.9.1:

Verification

Ran against a freshly installed node_modules (my local copy was stale, which had been masking two unrelated failures):

  • npm run typecheck — clean
  • npm run lint — clean, zero warnings
  • npm run format — no changes to make
  • npm test — 3314 tests, 0 failures, 185 skips (the usual local better-sqlite3 ABI skips)
  • npm run build:renderer — builds

Prettier is clean on all touched files. Worth noting separately: the repo's format script runs Prettier from inside src, so test/** is never covered by it — 38 test files are currently unformatted. Identical count before and after this change, so I left them alone rather than burying a release fix in reformatting noise.

Follow-ups, not in this PR

  • policyValidation.js:35 rejects forward-compatible policies. It validates server-sent provider ids against the client's own bundled registry, so a 1.9.0 client receiving gemini in allowedByokProviders fails isValidPolicyShape, workspacePolicyManager.js:288 throws "Malformed policy response", and policyRules.ts:24 fails closed — a personal account gets a locked model selector and a "Managed by your organization" badge. The same file already documents the right pattern for requiredLocalModels at lines 50-53, and both behaviors are pinned by contradictory tests (policyValidation.test.js:79 accepts unknown model ids, :121 rejects unknown providers). As written, every provider added server-side breaks every older client.
  • Main process should not load raw TS. electron-builder.json hand-enumerates src/stores/policyRules.ts and src/services/transcriptionBaseUrl.ts file by file, which is the packaging config signalling how fragile this is. Bundling main would remove the whole bug class.
  • The issue title says "text cleanup", but none of the three reachable handlers is cleanup — @lysacor's "re-transcribe a recording" is the accurate repro.

…ibute

policyRules.ts gained a modelRegistryData.json import in 1.9.1 (#1836)
without `with { type: "json" }`. ipcHandlers.js reaches that module through
Electron's bare Node ESM loader, which rejects attribute-less JSON imports,
so proxy-xai-transcription, retry-transcription and
transcribe-audio-file-byok threw ERR_IMPORT_ATTRIBUTE_MISSING in packaged
builds on every platform.

The existing tests import the same module but run under tsx, which resolves
those imports anyway, so they passed. The new test imports each
main-process ESM entry in a child process with no loader hooks, matching
what the packaged app actually does.

Prepares 1.9.2.

Fixes #1908
@gabrielste1n
gabrielste1n merged commit ca97c0b into main Aug 29, 2026
9 checks passed
@gabrielste1n
gabrielste1n deleted the fix/policy-rules-json-import-attribute branch August 29, 2026 16:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Text cleanup broken in packaged 1.9.x: modelRegistryData.json ESM-imported without with { type: "json" } (policyRules.ts / ModelRegistry.ts)

1 participant