{"record":{"id":"f46a622e47922ce6","repo":"can1357/oh-my-pi","slug":"result-cannot-contain-both-data-and-error","errorCode":null,"errorMessage":"result cannot contain both data and error","messagePattern":"result cannot contain both data and error","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/tools/yield.ts","lineNumber":382,"sourceCode":"\t\t_context?: AgentToolContext,\n\t): Promise<AgentToolResult<YieldDetails>> {\n\t\tconst raw = params as Record<string, unknown>;\n\t\tconst yieldType = parseYieldType(raw.type);\n\t\tconst resultRecord = resolveResultRecord(raw, yieldType);\n\t\tif (resultRecord === undefined) {\n\t\t\tthrow new Error(`result must be an object containing either data or error. ${YIELD_RESULT_FORMAT_HINT}`);\n\t\t}\n\t\tconst errorMessage = typeof resultRecord.error === \"string\" ? resultRecord.error : undefined;\n\t\tlet data = resultRecord.data;\n\t\tconst useLastTurn =\n\t\t\terrorMessage === undefined && data === undefined && yieldType !== undefined && !(\"error\" in resultRecord);\n\t\t// Incremental array-typed sections carry partial data (one finding, one\n\t\t// field) that cannot satisfy the full output schema; the assembled result\n\t\t// is validated as a whole at finalization (executor finalizeSubprocessOutput).\n\t\tconst isIncremental = Array.isArray(yieldType) && yieldType.length > 0;\n\n\t\tif (errorMessage !== undefined && data !== undefined) {\n\t\t\tthrow new Error(\"result cannot contain both data and error\");\n\t\t}\n\t\tif (errorMessage === undefined && data === undefined && yieldType === undefined) {\n\t\t\tthis.#emptyResultFailures++;\n\t\t\tif (this.#emptyResultFailures > MAX_EMPTY_RESULT_RETRIES) {\n\t\t\t\tconst attemptCount = this.#emptyResultFailures;\n\t\t\t\tthis.#emptyResultFailures = 0;\n\t\t\t\tconst error =\n\t\t\t\t\t`yield result stayed empty after ${attemptCount} consecutive attempt(s); aborting child instead of retrying forever. ` +\n\t\t\t\t\t'Submit success as `{ \"result\": { \"data\": <your output> } }` or failure as `{ \"result\": { \"error\": \"message\" } }`.';\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [{ type: \"text\", text: `Task aborted: ${error}` }],\n\t\t\t\t\tdetails: {\n\t\t\t\t\t\tdata: undefined,\n\t\t\t\t\t\tstatus: \"aborted\",\n\t\t\t\t\t\terror,\n\t\t\t\t\t\ttype: yieldType,\n\t\t\t\t\t},\n\t\t\t\t};","sourceCodeStart":364,"sourceCodeEnd":400,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/tools/yield.ts#L364-L400","documentation":"The yield tool for subagent results rejects a submission that supplies both `data` (success payload) and `error` (failure message) at once. A result is either a success or a failure — including both is ambiguous, so execute() throws early instead of guessing. The tool schema should already prevent this, but direct calls or malformed tool args can slip past validation.","triggerScenarios":"Calling the yield tool (via result/section/finalize/overrideResult) with both `data` and `error` defined in the result payload, e.g. {result: {data: {...}, error: \"something\"}}.","commonSituations":"A model or agent harness copies a result template and forgets to delete the `error` field; a wrapper always sets `error: undefined` but a bug turns it into a string like \"none\" or \"\" (non-undefined); hand-crafted RPC/tool-call payloads bypass schema validation.","solutions":["Remove the `error` field and submit {result: {data: ...}} for a successful outcome","Remove `data` and submit {result: {error: \"message\"}} if the task failed","Ensure failed/absent values are `undefined`, not empty strings or placeholder text, so the mutually-exclusive check passes","Fix the tool-call argument construction in the calling harness so it never emits both keys"],"exampleFix":"// before\nyield({ result: { data: { findings }, error: \"none\" } });\n// after\nyield({ result: { data: { findings } } });","handlingStrategy":"validation","validationCode":"function canYield(r) { return (r.data === undefined) !== (r.error === undefined); }\nif (!canYield(result)) throw new TypeError('yield result must have exactly one of data or error');","typeGuard":"function isExclusiveResult(r) {\n  const hasData = r.data !== undefined;\n  const hasError = r.error !== undefined;\n  return hasData !== hasError;\n}","tryCatchPattern":"try {\n  yield({ result });\n} catch (err) {\n  if (err.message.includes('both data and error')) {\n    yield({ result: result.error !== undefined ? { error: result.error } : { data: result.data } });\n  } else throw err;\n}","preventionTips":["Always build yield results through a single helper that picks data OR error","Never set error to placeholder strings like \"none\" or \"\" — use undefined","Validate tool args against the yield schema before submission"],"tags":["tool-call","yield","api-misuse","subagent"],"backgroundTag":"mutually-exclusive-parameters","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}