Automattic/harper · error · Error

Unhandled case: ${arg}

Error message

Unhandled case: ${arg}

What it means

serializeArg's final fallback: when an argument is not an Array, not string/number/boolean/undefined/bigint, has no to_json() method, and is not a plain object, the serializer has no way to represent it on the worker wire and throws 'Unhandled case: <arg>'. This rejects unsupported argument kinds (functions, symbols, Maps, Sets, class instances without to_json, etc.) at RPC time.

Source

Thrown at packages/harper.js/src/Serializer.ts:103

			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}`);
	}

	async serialize(req: DeserializedRequest): Promise<SerializedRequest> {
		return {
			procName: req.procName,
			args: await Promise.all(req.args.map((arg) => this.serializeArg(arg))),
		};
	}

	async deserializeArg(requestArg: RequestArg): Promise<any> {
		const { Lint, Span, Suggestion } = await this.binary.getBinaryModule();

		switch (requestArg.type) {
			case 'bigint':
				return BigInt(requestArg.json);
			case 'undefined':
				return undefined;
			case 'boolean':

View on GitHub (pinned to 5fe7d5ab76)

Solutions

  1. Convert the argument to a supported type before the call: plain object, array, string, number, boolean, bigint, or undefined
  2. Replace Map/Set with plain objects/arrays; convert Date to a number/string
  3. Remove callbacks or non-serializable values from the arguments
  4. If a needed type is missing, serialize it yourself (e.g. Array.from(map.entries())) or file an issue to extend Serializer

Example fix

// before
await linter.lint(text, [new Map([['a', 1]])]);
// after
await linter.lint(text, [{ a: 1 }]);
Defensive patterns

Strategy: validation

Validate before calling

function isRpcSerializable(v: unknown): boolean {
  return v === undefined || ['string','number','boolean','bigint'].includes(typeof v)
    || Array.isArray(v)
    || (typeof v === 'object' && v !== null && (typeof (v as any).to_json === 'function'
      || v.constructor?.name === 'Object'));
}
args.every(isRpcSerializable) || console.error('unsupported WorkerLinter arg', args);

Try / catch

try {
  return await linter.lint(text);
} catch (e) {
  if (e instanceof Error && e.message.startsWith('Unhandled case:')) {
    console.error('Unsupported argument passed to WorkerLinter; normalize inputs.');
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling a WorkerLinter method (lint, isLikelyEnglish, applySuggestion, ...) with arguments like a function, Symbol, Map/Set, DOM node, or a class instance lacking to_json() — anything outside the SerializableTypes surface.

Common situations: Passing a Map<string, ...> where a plain object was expected; accidentally passing a callback or Promise into lint config arguments; passing typed arrays or Dates.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


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