Automattic/harper · error · Error

Unhandled case: ${requestArg.type}

Error message

Unhandled case: ${requestArg.type}

What it means

deserializeArg (worker side) switches on the wire tag (RequestArg.type) sent by the main thread. If the tag is not one of the known SerializableTypes — typically because main and worker threads run mismatched harper.js versions or a corrupted/hand-crafted message was posted — the switch falls through to default and throws 'Unhandled case: <type>'.

Source

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

			case 'Suggestion':
				return Suggestion.from_json(requestArg.json);
			case 'Lint':
				return Lint.from_json(requestArg.json);
			case 'Span':
				return Span.from_json(requestArg.json);
			case 'Array': {
				const parsed = JSON.parse(requestArg.json);
				assert(Array.isArray(parsed));
				return await Promise.all(parsed.map((arg) => this.deserializeArg(arg)));
			}
			case 'object': {
				const parsed = JSON.parse(requestArg.json);
				return Object.fromEntries(
					await Promise.all(parsed.map((val: any) => this.deserializeArg(val))),
				);
			}
			default:
				throw new Error(`Unhandled case: ${requestArg.type}`);
		}
	}

	async deserialize(request: SerializedRequest): Promise<DeserializedRequest> {
		return {
			procName: request.procName,
			args: await Promise.all(request.args.map((arg) => this.deserializeArg(arg))),
		};
	}
}

View on GitHub (pinned to 5fe7d5ab76)

Solutions

  1. Rebuild and redeploy so main thread and worker bundle the same harper.js version (bust caches/service worker)
  2. Check the offending type tag in the message and ensure it is one of the documented SerializableTypes
  3. Stop hand-crafting or mutating SerializedRequest objects; use the public WorkerLinter API
  4. Clear stale worker caches (service workers, HTTP cache) so old chunks don't mix with new

Example fix

// before (hand-built message)
worker.postMessage({ procName: 'lint', args: [{ json: 'x', type: 'Date' }] });
// after
const linter = new WorkerLinter();
await linter.lint('x');
Defensive patterns

Strategy: validation

Validate before calling

const KNOWN_TYPES = ['string','number','boolean','object','Suggestion','Lint','Span','Array','undefined','bigint'];
function isValidSerializedRequest(req: any): boolean {
  return req?.args?.every((a: any) => KNOWN_TYPES.includes(a?.type)) ?? false;
}
isValidSerializedRequest(request) || console.error('unknown arg type in request');

Type guard

const KNOWN_TYPES = ['string','number','boolean','object','Suggestion','Lint','Span','Array','undefined','bigint'] as const;
function isKnownRequestArg(a: unknown): a is RequestArg {
  return !!a && typeof a === 'object' && KNOWN_TYPES.includes((a as any).type);
}

Try / catch

try {
  return await linter.lint(text);
} catch (e) {
  if (e instanceof Error && e.message.startsWith('Unhandled case:')) {
    // version skew or corrupted message; recreate the worker
    linter.dispose();
    linter = new WorkerLinter({ binary: wasmUrl });
  }
  throw e;
}

Prevention

When it happens

Trigger: A SerializedRequest or nested array/object element arrives with a type tag the worker's Serializer doesn't know (e.g. 'Date', 'Map', or a tag added in a newer harper.js running against an older worker build); manually posting messages to the worker; corrupted postMessage payloads.

Common situations: Version skew between bundled main-thread harper.js and a cached/split worker chunk; custom code speaking the worker protocol directly; HMR/service-worker caches mixing old and new chunks.

Understand the failure class

Background: "failed to unmarshal" / json.Unmarshal errors: why parsing a response into a Go struct fails and how to fix it — this error's family across 23 libraries.

Related errors


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