{"record":{"id":"588e4cb5ad4565be","repo":"vercel/ai","slug":"harnessagent-pass-either-activetools-or-inact","errorCode":null,"errorMessage":"'HarnessAgent: pass either `activeTools` or `inactiveTools`, not both.'","messagePattern":"'HarnessAgent: pass either `activeTools` or `inactiveTools`, not both\\.'","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/harness/src/agent/internal/tool-filtering.ts","lineNumber":22,"sourceCode":"import type { ToolSet } from '@ai-sdk/provider-utils';\n\nexport type ResolvedHarnessAgentToolFiltering<TUserTools extends ToolSet> = {\n  readonly activeUserTools: TUserTools;\n  readonly builtinToolFiltering?: HarnessV1BuiltinToolFiltering;\n};\n\nexport function resolveHarnessAgentToolFiltering<\n  TAllTools extends ToolSet,\n  TUserTools extends ToolSet,\n>(input: {\n  harness: HarnessV1;\n  userTools: TUserTools;\n  allTools: TAllTools;\n  activeTools: ActiveTools<TAllTools>;\n  inactiveTools: ActiveTools<TAllTools>;\n}): ResolvedHarnessAgentToolFiltering<TUserTools> {\n  if (input.activeTools !== undefined && input.inactiveTools !== undefined) {\n    throw new Error(\n      'HarnessAgent: pass either `activeTools` or `inactiveTools`, not both.',\n    );\n  }\n\n  const allToolNames = Object.keys(input.allTools);\n  const activeTools = dedupeToolNames({ toolNames: input.activeTools });\n  const inactiveTools = dedupeToolNames({ toolNames: input.inactiveTools });\n  validateToolNames({\n    requestedToolNames: activeTools ?? inactiveTools,\n    availableToolNames: allToolNames,\n  });\n\n  const userToolNames = Object.keys(input.userTools);\n  const activeUserToolNames =\n    activeTools != null\n      ? userToolNames.filter(name => activeTools.includes(name))\n      : inactiveTools != null\n        ? userToolNames.filter(name => !inactiveTools.includes(name))","sourceCodeStart":4,"sourceCodeEnd":40,"githubUrl":"https://github.com/vercel/ai/blob/69428b1f8b037e4d118fb4853428d5c4e620493c/packages/harness/src/agent/internal/tool-filtering.ts#L4-L40","documentation":"resolveHarnessAgentToolFiltering accepts exactly one of `activeTools` (allow-list) or `inactiveTools` (deny-list). Passing both is ambiguous — the library cannot decide whether to intersect or prioritize them, so it throws immediately.","triggerScenarios":"Creating a HarnessAgent (or calling toolFiltering config) with both activeTools: ['read','write'] and inactiveTools: ['shell'] set simultaneously.","commonSituations":"Merging two config objects where each sets a different tool list; spreading defaults that include inactiveTools on top of user-supplied activeTools.","solutions":["Remove one of the two options — keep activeTools to allow-list or inactiveTools to deny-list.","If both lists come from merged configs, convert: inactiveTools = allToolNames.filter(n => !activeTools.includes(n)) and pass only one.","Compute the effective list yourself and pass a single array."],"exampleFix":"// before\nnew HarnessAgent({ activeTools: ['read', 'write'], inactiveTools: ['shell'] });\n// after\nnew HarnessAgent({ activeTools: ['read', 'write'] }); // or inactiveTools only","handlingStrategy":"validation","validationCode":"if (config.activeTools != null && config.inactiveTools != null) throw new Error('use activeTools or inactiveTools, not both');","typeGuard":null,"tryCatchPattern":"try { createAgent(config); } catch (e) { if (e.message.includes('not both')) { config = { ...config, inactiveTools: undefined }; createAgent(config); } else throw e; }","preventionTips":["Pick one filtering style (allow-list or deny-list) per project and standardize on it","When merging configs, delete the opposite key","Add a config schema check before agent construction"],"tags":["configuration","tools","validation"],"backgroundTag":"mutually-exclusive-options","analyzedSha":"69428b1f8b037e4d118fb4853428d5c4e620493c","analyzedAt":"2026-08-30T12:32:21.016Z","schemaVersion":2},"datasetVersion":"2026-08-30T13:17:10.514Z"}