can1357/oh-my-pi · error · ToolError

${XD_URL_PREFIX}${device.name} expects a JSON args object as

Error message

${XD_URL_PREFIX}${device.name} expects a JSON args object as content (${error instanceof Error ? error.message : String(error)}). Write `?` for docs.

What it means

xd:// devices take their arguments as a JSON object carried in the write `content`. parseDeviceArgs runs JSON.parse on the content, and if it is not valid JSON it throws this ToolError naming the device, the JSON parse error, and a hint to write `?` as content to fetch the device's docs.

Source

Thrown at packages/coding-agent/src/tools/xdev.ts:159

}

/**
 * Parse and validate a device write's JSON `content` against the wrapped
 * tool's wire schema. Strips a habitual top-level `i` (intent) unless the
 * schema declares one. Throws ToolError; schema-mismatch errors carry `docs()`
 * for repair.
 */
function parseDeviceArgs(
	device: AiTool,
	content: string,
	toolCallId: string,
	docs: () => string,
): Record<string, unknown> {
	let parsed: unknown;
	try {
		parsed = JSON.parse(content);
	} catch (error) {
		throw new ToolError(
			`${XD_URL_PREFIX}${device.name} expects a JSON args object as content (${error instanceof Error ? error.message : String(error)}). Write \`?\` for docs.`,
		);
	}
	if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
		throw new ToolError(
			`${XD_URL_PREFIX}${device.name} content must be a JSON object, got ${Array.isArray(parsed) ? "array" : typeof parsed}.`,
		);
	}
	// The harness only injects the intent field into top-level schemas; strip a
	// habitual `i` from inner args unless the wrapped schema really declares it.
	const args: Record<string, unknown> = { ...(parsed as Record<string, unknown>) };
	if ("i" in args && !schemaDeclaresIntentField(toolWireSchema(device))) delete args.i;
	try {
		return validateToolArguments(device, {
			type: "toolCall",
			id: toolCallId,
			name: device.name,
			arguments: args,

View on GitHub (pinned to 9690622007)

Solutions

  1. Send the arguments as a valid JSON object, e.g. {"key": "value"}.
  2. Write `?` as content to `xd://<device>` to retrieve the device's docs and schema.
  3. Validate the content with JSON.parse (or a linter) before the write.
  4. Remove non-JSON decoration: no comments, trailing commas, single quotes, or surrounding prose.

Example fix

// before
write({ path: "xd://browser", content: "navigate to example.com" })
// after
write({ path: "xd://browser", content: "{\"action\":\"navigate\",\"url\":\"https://example.com\"}" })
Defensive patterns

Strategy: validation

Validate before calling

try { JSON.parse(content); } catch (err) { throw new Error(`xd:// content must be valid JSON: ${err.message}`); }

Type guard

function isJsonString(s: string): boolean { try { JSON.parse(s); return true; } catch { return false; } }

Try / catch

try { await write({ path: `xd://${device}`, content }); } catch (e) { if (String(e.message).includes("expects a JSON args object")) { const docs = await write({ path: `xd://${device}`, content: "?" }); /* rebuild JSON args from docs */ } else throw e; }

Prevention

When it happens

Trigger: Calling Write to `xd://<device>` with content that is not parseable JSON — e.g. plain prose, a shell-style flag string, trailing commas, single quotes, or markdown wrapping the JSON.

Common situations: An agent writes natural-language instructions or key=value text to an xd:// device instead of a JSON object; or the JSON got mangled by escaping/quoting issues.

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/5bd602d6e309148f. Report an issue: GitHub.