{"record":{"id":"99ba3173c566f58f","repo":"tinyhumansai/openhuman","slug":"agentworkapi-control-args-action-requires-a-me","errorCode":null,"errorMessage":"agentWorkApi.control: ${args.action} requires a message","messagePattern":"agentWorkApi\\.control: (.+?) requires a message","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"app/src/services/api/agentWorkApi.ts","lineNumber":110,"sourceCode":"      params: limit === undefined ? {} : { limit },\n    });\n    log('list received groups=%d total=%d', response.groups.length, response.total);\n    return response;\n  },\n\n  /**\n   * Apply a control verb to one background agent run, returning the updated row.\n   *\n   * `continue` and `follow_up` carry the user's message; the client rejects an\n   * empty message for those verbs before hitting core (the Rust handler also\n   * enforces it). `stop` may carry an optional `reason`.\n   */\n  control: async (args: AgentWorkControlArgs): Promise<AgentWorkRow> => {\n    const runId = args.runId?.trim();\n    if (!runId) throw new Error('agentWorkApi.control: runId is required');\n    const message = args.message?.trim();\n    if ((args.action === 'continue' || args.action === 'follow_up') && !message) {\n      throw new Error(`agentWorkApi.control: ${args.action} requires a message`);\n    }\n    const params: Record<string, unknown> = { runId, action: args.action };\n    if (message) params.message = message;\n    const reason = args.reason?.trim();\n    if (reason) params.reason = reason;\n    log('control runId=%s action=%s', runId, args.action);\n    const response = await callCoreRpc<AgentWorkControlResponse>({\n      method: 'openhuman.agent_work_control',\n      params,\n    });\n    log('control received status=%s', response.row.status);\n    return response.row;\n  },\n};\n","sourceCodeStart":92,"sourceCodeEnd":125,"githubUrl":"https://github.com/tinyhumansai/openhuman/blob/a221052e0df5b1f7598fceba7329fd1af95d6699/app/src/services/api/agentWorkApi.ts#L92-L125","documentation":"Guard inside agentWorkApi.control: the verbs 'continue' and 'follow_up' semantically require a user message (the run cannot be continued with nothing to say), so the client rejects them when message is missing or whitespace-only after trimming. The Rust handler enforces the same rule server-side; this is the early client check.","triggerScenarios":"Calling control({ runId, action: 'continue' }) with no message, or with message: '   ' — e.g. a dialog whose textarea is submitted empty, or a send handler that forwards an unvalidated string.","commonSituations":"An 'Ask follow-up' modal whose submit button is not disabled on empty input; prefilled draft cleared by the user; message passed as an optional field and left undefined by a wrapper function.","solutions":["Validate non-empty trimmed input in the dialog before calling (and disable submit when empty)","Trim the textarea value at the call boundary: const msg = input.trim(); if (!msg) return;","For 'stop', keep using the optional reason field — no message needed there"],"exampleFix":"// before\nawait agentWorkApi.control({ runId, action: 'follow_up', message: draft }); // draft === '  '\n\n// after\nconst msg = draft.trim();\nif (!msg) return showError('Enter a message to continue this run');\nawait agentWorkApi.control({ runId, action: 'follow_up', message: msg });","handlingStrategy":"validation","validationCode":"const message = args?.message?.trim() ?? '';\nif (action === 'stop' || message) {\n  await agentWorkApi.control({ runId, action, ...(message ? { message } : {}) });\n}","typeGuard":"const isActionMessage = (a: string, m: unknown): boolean =>\n  a === 'continue' || a === 'follow_up' ? typeof m === 'string' && m.trim().length > 0 : true;","tryCatchPattern":"try { await agentWorkApi.control({ runId, action, message }); }\ncatch (e) { if (String(e.message).includes('requires a message')) showInputError('Enter a message'); else throw e; }","preventionTips":["Disable submit on empty textarea for continue/follow-up dialogs","Trim input at the boundary and reject early with UI feedback","Remember 'stop' needs no message — only reason (optional)"],"tags":["validation","agent-work","required-field"],"backgroundTag":null,"analyzedSha":"a221052e0df5b1f7598fceba7329fd1af95d6699","analyzedAt":"2026-08-16T12:47:06.542Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}