Automattic/harper · error · Error
Unhandled case: type undefined
Error message
Unhandled case: type undefined
What it means
WorkerLinter serializes every RPC argument with Serializer.serializeArg. Arguments carrying a to_json() method (WASM-bound objects) must be recognizable as a Lint, Suggestion, or Span; if an object exposes to_json() but its class/constructor is none of those three, the serializer cannot pick a wire type tag and throws 'Unhandled case: type undefined'. This guards against silently mis-typing WASM objects crossing the worker boundary.
Source
Thrown at packages/harper.js/src/Serializer.ts:86
case 'bigint':
return { json: arg.toString(), type: argType };
}
if (arg.to_json !== undefined) {
const json = arg.to_json();
let type: SerializableTypes | undefined;
const constructorName = arg.constructor?.name;
if (arg instanceof Lint || constructorName === 'Lint') {
type = 'Lint';
} else if (arg instanceof Suggestion || constructorName === 'Suggestion') {
type = 'Suggestion';
} else if (arg instanceof Span || constructorName === 'Span') {
type = 'Span';
}
if (type === undefined) {
throw new Error('Unhandled case: type undefined');
}
return { json, type };
}
if (argType == 'object') {
return {
json: JSON.stringify(
await Promise.all(
Object.entries(arg).map(([key, value]) => this.serializeArg([key, value])),
),
),
type: 'object',
};
}
throw new Error(`Unhandled case: ${arg}`);
}View on GitHub (pinned to 5fe7d5ab76)
Solutions
- Check that the argument is actually a harper-wasm Lint, Suggestion, or Span from the same WASM binary the WorkerLinter was created with
- Ensure your bundler does not mangle class names of harper-wasm exports (configure keep_fnames / class-name preservation)
- Align harper.js and harper-wasm package versions (same release line)
- Pass only primitives, plain objects/arrays, or documented Harper types to WorkerLinter methods
Example fix
// before
linter.lint("text", [someUnknownWasmObject]);
// after
import { Span } from 'harper.js';
const span = new Span(...); // a real Span from the same harper-wasm build
linter.lint("text", [span]); Defensive patterns
Strategy: type-guard
Validate before calling
const SUPPORTED = ['Lint', 'Span', 'Suggestion'];
function isSerializableHarperObject(arg: unknown): boolean {
const anyArg = arg as any;
return typeof anyArg?.to_json !== 'function' ||
SUPPORTED.includes(anyArg?.constructor?.name);
}
args.every(isSerializableHarperObject) || console.error('unserializable arg', args); Type guard
function isKnownWasmType(arg: any): arg is Lint | Span | Suggestion {
return ['Lint', 'Span', 'Suggestion'].includes(arg?.constructor?.name);
} Try / catch
try {
await linter.lint(text, args);
} catch (e) {
if (e instanceof Error && e.message === 'Unhandled case: type undefined') {
console.error('Non-serializable WASM object passed to WorkerLinter:', args);
}
throw e;
} Prevention
- Only pass primitives, plain objects/arrays, or documented Harper types to WorkerLinter methods
- Keep harper.js and harper-wasm versions in lockstep
- Configure your minifier to preserve harper-wasm class names
- Wrap linter calls in a small typed API surface rather than passing arbitrary values
When it happens
Trigger: Calling any WorkerLinter method (lint, applySuggestion, etc.) passing a WASM-bound object with a to_json() method whose class is not Lint, Suggestion, or Span — e.g. a custom/mis-minified class name breaking the constructorName === fallback, a stale or mismatched harper-wasm binary version, or wrapping a WASM object in a subclass.
Common situations: Bundlers/minifiers (Terser, esbuild) renaming the Lint/Span/Suggestion constructors so constructorName no longer matches and instanceof fails against a different WASM instance; mixing harper.js and harper-wasm versions; passing non-standard Harper objects into linter calls.
Understand the failure class
Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.
Related errors
- Unhandled case: ${arg}
- Unhandled case: ${requestArg.type}
- Expected binary to be a string of url but got ${typeof binar
- Expected glue flavor to be "full" or "slim" but got ${glueFl
- WorkerLinter has been disposed.
AI-assisted analysis of Automattic/harper@5fe7d5ab76 (2026-09-06).
Data as JSON: /api/errors/52b769997b7cea4e.
Report an issue: GitHub.