different-ai/openwork · error
workflow_test_capability_mismatch
workflow_test_capability_mismatch
Error message
workflow_test_capability_mismatch:${call.name} What it means
Thrown in createWorkflowVersion when a tool call recorded in the test receipt (receipt.tool_calls) does not correspond to any entry in payload.parsed.requiredCapabilities, after normalizing the 'tools.' prefix. The declared capability list and the calls actually made during the test run must agree exactly.
Solutions
- Re-run the workflow test with the current script so requiredCapabilities covers every tool call it makes.
- Add the missing tool path to requiredCapabilities (ensuring it is available and read-only).
- Remove or guard the extra tool call in the workflow script.
Example fix
// before
requiredCapabilities: [{ scriptPath: "tools.search.web", capabilityName: "webSearch" }]
// script also calls tools.docs.list
// after
requiredCapabilities: [
{ scriptPath: "tools.search.web", capabilityName: "webSearch" },
{ scriptPath: "tools.docs.list", capabilityName: "docsList" },
] Defensive patterns
Strategy: validation
Validate before calling
const calls = parseCodemodeToolCalls(receipt.tool_calls).map(c => c.name)
const declared = new Set(requiredCapabilities.flatMap(r => [r.scriptPath, r.scriptPath.replace(/^tools\./, "")]))
const undeclared = calls.filter(name => !declared.has(name) && !declared.has(name.replace(/^tools\./, "")))
if (undeclared.length) throw new Error(`undeclared calls: ${undeclared}`) Try / catch
try {
await createWorkflowVersion(input)
} catch (e) {
if (String(e.message).startsWith("workflow_test_capability_mismatch:")) {
const tool = e.message.split(":")[1]
// declare `tool` in requiredCapabilities or remove the call
} else throw e
} Prevention
- Regenerate requiredCapabilities by re-running tests whenever the script changes.
- Keep scripts free of conditional tool calls outside the declared set.
- Diff receipt.tool_calls against requiredCapabilities before registering.
When it happens
Trigger: Registering a workflow where the test run invoked a tool that is not declared in requiredCapabilities — e.g. capabilities were edited after the test ran, or the script changed between test and registration.
Common situations: Hand-editing the workflow payload to trim capability declarations; a script that conditionally calls extra tools; stale test receipt from an older script revision.
Related errors
- workflow_capability_unavailable
- workflow_requires_read_only_capabilities
- workflow_snapshot_not_found
- workflow_test_receipt_already_used
- automation_owner_inactive
AI-assisted analysis of different-ai/openwork@2b7df46e8a (2026-09-01).
Data as JSON: /api/errors/86a840f4b28b084a.
Report an issue: GitHub.
Appendix: source
Thrown at ee/apps/den-api/src/workflows.ts:428
if (consumed[0]) throw new Error("workflow_test_receipt_already_used")
const built = await input.buildTools()
const manifestByPath = new Map(built.manifest.flatMap((entry) => [
[entry.scriptPath, entry] as const,
[entry.scriptPath.replace(/^tools\./, ""), entry] as const,
]))
for (const required of payload.parsed.requiredCapabilities) {
const current = manifestByPath.get(required.scriptPath)
if (!current || current.capabilityName !== required.capabilityName) {
throw new Error(`workflow_capability_unavailable:${required.scriptPath}`)
}
if (current.readOnly !== true) throw new Error(`workflow_requires_read_only_capabilities:${required.scriptPath}`)
}
for (const call of parseCodemodeToolCalls(receipt.tool_calls)) {
if (!payload.parsed.requiredCapabilities.some((required) => {
const normalized = call.name.replace(/^tools\./, "")
return required.scriptPath === call.name || required.scriptPath.replace(/^tools\./, "") === normalized
})) throw new Error(`workflow_test_capability_mismatch:${call.name}`)
}
const now = new Date()
const configObjectVersionId = createDenTypeId("configObjectVersion")
await db.transaction(async (tx) => {
await tx.insert(ConfigObjectVersionTable).values({
id: configObjectVersionId,
organizationId: resource.configObject.organizationId,
configObjectId: resource.configObject.id,
normalizedPayloadJson: payload.value,
rawSourceText: input.draft.code,
schemaVersion: "codemode-script-v1",
createdVia: "cloud",
createdByOrgMembershipId: input.context.organizationContext.currentMember.id,
sourceRevisionRef: receipt.id,
isDeletedVersion: false,
createdAt: now,
})View on GitHub (pinned to 2b7df46e8a)