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

  1. Check that the argument is actually a harper-wasm Lint, Suggestion, or Span from the same WASM binary the WorkerLinter was created with
  2. Ensure your bundler does not mangle class names of harper-wasm exports (configure keep_fnames / class-name preservation)
  3. Align harper.js and harper-wasm package versions (same release line)
  4. 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

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


AI-assisted analysis of Automattic/harper@5fe7d5ab76 (2026-09-06). Data as JSON: /api/errors/52b769997b7cea4e. Report an issue: GitHub.