vercel/ai · error
'HarnessAgent: pass either `activeTools` or `inactiveTools`,
Error message
'HarnessAgent: pass either `activeTools` or `inactiveTools`, not both.'
What it means
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.
Source
Thrown at packages/harness/src/agent/internal/tool-filtering.ts:22
import type { ToolSet } from '@ai-sdk/provider-utils';
export type ResolvedHarnessAgentToolFiltering<TUserTools extends ToolSet> = {
readonly activeUserTools: TUserTools;
readonly builtinToolFiltering?: HarnessV1BuiltinToolFiltering;
};
export function resolveHarnessAgentToolFiltering<
TAllTools extends ToolSet,
TUserTools extends ToolSet,
>(input: {
harness: HarnessV1;
userTools: TUserTools;
allTools: TAllTools;
activeTools: ActiveTools<TAllTools>;
inactiveTools: ActiveTools<TAllTools>;
}): ResolvedHarnessAgentToolFiltering<TUserTools> {
if (input.activeTools !== undefined && input.inactiveTools !== undefined) {
throw new Error(
'HarnessAgent: pass either `activeTools` or `inactiveTools`, not both.',
);
}
const allToolNames = Object.keys(input.allTools);
const activeTools = dedupeToolNames({ toolNames: input.activeTools });
const inactiveTools = dedupeToolNames({ toolNames: input.inactiveTools });
validateToolNames({
requestedToolNames: activeTools ?? inactiveTools,
availableToolNames: allToolNames,
});
const userToolNames = Object.keys(input.userTools);
const activeUserToolNames =
activeTools != null
? userToolNames.filter(name => activeTools.includes(name))
: inactiveTools != null
? userToolNames.filter(name => !inactiveTools.includes(name))View on GitHub (pinned to 69428b1f8b)
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.
Example fix
// before
new HarnessAgent({ activeTools: ['read', 'write'], inactiveTools: ['shell'] });
// after
new HarnessAgent({ activeTools: ['read', 'write'] }); // or inactiveTools only Defensive patterns
Strategy: validation
Validate before calling
if (config.activeTools != null && config.inactiveTools != null) throw new Error('use activeTools or inactiveTools, not both'); Try / catch
try { createAgent(config); } catch (e) { if (e.message.includes('not both')) { config = { ...config, inactiveTools: undefined }; createAgent(config); } else throw e; } Prevention
- 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
When it happens
Trigger: Creating a HarnessAgent (or calling toolFiltering config) with both activeTools: ['read','write'] and inactiveTools: ['shell'] set simultaneously.
Common situations: Merging two config objects where each sets a different tool list; spreading defaults that include inactiveTools on top of user-supplied activeTools.
Related errors
- Invalid argument for parameter output: Invalid output type.
- unknown tool "${toolName}".
- callers for tool "${toolName}" must be an array.
- tool "${toolName}" contains an invalid caller.
- Continuation maxAgeMs must be a positive integer.
AI-assisted analysis of vercel/ai@69428b1f8b (2026-08-30).
Data as JSON: /api/errors/588e4cb5ad4565be.
Report an issue: GitHub.