{"record":{"id":"671ef504de041e9b","repo":"can1357/oh-my-pi","slug":"keys-must-be-a-string","errorCode":null,"errorMessage":"keys must be a string","messagePattern":"keys must be a string","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/tui/src/debug-server.ts","lineNumber":353,"sourceCode":"\t\t\t\treturn { ok: true, tree: this.#tree() };\n\t\t\tcase \"values\":\n\t\t\t\treturn { ok: true, values: this.#values() };\n\t\t\tcase \"info\": {\n\t\t\t\tconst paint = this.#tui.getDebugPaint();\n\t\t\t\tconst focused = this.#tui.getFocused();\n\t\t\t\treturn {\n\t\t\t\t\tok: true,\n\t\t\t\t\tcolumns: this.#tui.terminal.columns,\n\t\t\t\t\trows: this.#tui.terminal.rows,\n\t\t\t\t\tpid: process.pid,\n\t\t\t\t\toverlays: this.#tui.overlayStack.length,\n\t\t\t\t\tfocused: focused === null ? null : componentKind(focused),\n\t\t\t\t\talt_screen: paint?.altScreen ?? false,\n\t\t\t\t\tcursor: paint?.cursor ?? { visible: false },\n\t\t\t\t};\n\t\t\t}\n\t\t\tcase \"keys\": {\n\t\t\t\tif (typeof request.keys !== \"string\") throw new Error(\"keys must be a string\");\n\t\t\t\tconst parsed = parseKeyTokens(request.keys);\n\t\t\t\tfor (const sequence of parsed.sequences) this.#tui.injectDebugInput(sequence);\n\t\t\t\treturn { ok: true, injected: parsed.events };\n\t\t\t}\n\t\t\tcase \"bytes\":\n\t\t\t\tif (typeof request.data !== \"string\") throw new Error(\"data must be a string\");\n\t\t\t\tthis.#tui.injectDebugInput(request.data);\n\t\t\t\treturn { ok: true };\n\t\t\tcase \"paste\":\n\t\t\t\tif (typeof request.text !== \"string\") throw new Error(\"text must be a string\");\n\t\t\t\tthis.#tui.injectDebugInput(`\\x1b[200~${request.text}\\x1b[201~`);\n\t\t\t\treturn { ok: true };\n\t\t\tcase \"mouse\": {\n\t\t\t\tif (typeof request.x !== \"number\" || typeof request.y !== \"number\")\n\t\t\t\t\tthrow new Error(\"mouse x and y must be numbers\");\n\t\t\t\tconst action = request.action === undefined ? \"click\" : request.action;\n\t\t\t\tif (typeof action !== \"string\") throw new Error(\"mouse action must be a string\");\n\t\t\t\tthis.#tui.injectDebugInput(mouseSequence(request.x, request.y, action));","sourceCodeStart":335,"sourceCodeEnd":371,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/tui/src/debug-server.ts#L335-L371","documentation":"For the debug server's \"keys\" operation, #dispatch requires request.keys to be a string of key tokens (e.g. \"ctrl+c\", \"a b\", \"'x'\"). If keys is missing or of any other type (number, array, null), this error is thrown and returned to the client as a failed response. This is per-request payload validation before parseKeyTokens runs.","triggerScenarios":"Sending {\"op\":\"keys\"} without a keys field; sending keys as an array like [\"ctrl\",\"c\"]; sending keys as a number or null.","commonSituations":"Client SDKs that JSON-encode a key list instead of joining it into a token string; omitting the field because the client assumed a default; copy-pasted request templates with a different op.","solutions":["Set keys to a single string of whitespace-separated key tokens, e.g. \"ctrl+c a 'x'\"","Join a key array into a space-separated string before sending","Check the request field name is exactly keys for the keys op"],"exampleFix":"// before\n{ \"op\": \"keys\", \"keys\": [\"ctrl\", \"c\"] }\n// after\n{ \"op\": \"keys\", \"keys\": \"ctrl+c\" }","handlingStrategy":"validation","validationCode":"if (typeof request.keys !== \"string\")\n  throw new TypeError(`keys op requires a string, got ${typeof request.keys}`);","typeGuard":"function hasStringKeys(r: { op: string; keys?: unknown }): r is { op: \"keys\"; keys: string } {\n  return r.op === \"keys\" && typeof r.keys === \"string\";\n}","tryCatchPattern":"try {\n  const res = await send({ op: \"keys\", keys });\n} catch (err) {\n  if (String(err.message) === \"keys must be a string\") {\n    keys = String(keys);\n  }\n}","preventionTips":["Join key arrays client-side: Array.isArray(keys) ? keys.join(\" \") : keys","Use the same request-schema type on both client and server","Check the response's error field for per-request validation failures"],"tags":["validation","types","debug-server"],"backgroundTag":"invalid-request-field-type","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}