eyaltoledano/claude-task-master · error
OPERATION_NOT_FOUND
OPERATION_NOT_FOUND
Error message
Operation ID not found: ${operationId} What it means
get-operation-status was asked for an operation ID the AsyncOperationManager has no record of, so getStatus returned status='not_found' and the tool converts it into a structured OPERATIONAL error response with code OPERATION_NOT_FOUND. Operation IDs are in-memory and ephemeral, so unknown IDs are expected after restarts.
Source
Thrown at mcp-server/src/tools/get-operation-status.js:31
description:
'Retrieves the status and result/error of a background operation.',
parameters: z.object({
operationId: z.string().describe('The ID of the operation to check.')
}),
annotations: {
title: 'Get Operation Status',
readOnlyHint: true
},
execute: async (args, { log }) => {
try {
const { operationId } = args;
log.info(`Checking status for operation ID: ${operationId}`);
const status = asyncManager.getStatus(operationId);
// Status will now always return an object, but it might have status='not_found'
if (status.status === 'not_found') {
log.warn(`Operation ID not found: ${operationId}`);
return createErrorResponse(
status.error?.message || `Operation ID not found: ${operationId}`,
status.error?.code || 'OPERATION_NOT_FOUND'
);
}
log.info(`Status for ${operationId}: ${status.status}`);
return createContentResponse(status);
} catch (error) {
log.error(`Error in get_operation_status tool: ${error.message}`, {
stack: error.stack
});
return createErrorResponse(
`Failed to get operation status: ${error.message}`,
'GET_STATUS_ERROR'
);
}
}View on GitHub (pinned to c0c98d367c)
Solutions
- Re-issue the original operation (start the long-running tool again) and use the freshly returned operation ID.
- Verify the operationId string exactly matches the one returned when the operation was started.
- If IDs are lost across restarts, restart the workflow rather than polling; there is no persistent operation store.
Example fix
// before
await getOperationStatus({ operationId: "op_17123" }); // stale after restart
// after
const op = await startLongOperation(...);
await getOperationStatus({ operationId: op.operationId }); Defensive patterns
Strategy: validation
Validate before calling
if (!operationId || typeof operationId !== 'string' || !/^op[_-]?\w+$/i.test(operationId)) {
throw new Error(`Invalid or missing operationId: ${operationId}`);
}
// keep operation IDs only for the lifetime of the current server session Type guard
function isOperationId(v) {
return typeof v === 'string' && v.length > 0 && v === storedOperationId;
} Try / catch
const res = await getOperationStatus({ operationId });
if (res.status === 'not_found' || res.code === 'OPERATION_NOT_FOUND') {
// re-issue the operation rather than retrying the poll
const op = await startOperationAgain();
} Prevention
- Store the operation ID returned by the starting tool immediately and only use that value.
- Never reuse operation IDs across MCP server restarts; re-run the operation instead.
- Copy-paste the ID rather than retyping it.
When it happens
Trigger: Calling get-operation-status with an operationId that was never created, a mistyped ID, or an ID from a previous MCP server session (async operations are held in memory and lost on restart or manager reset).
Common situations: Client cached an operation ID across an MCP server restart; typo when copying the ID from start-operation output; polling long after the operation completed and the manager pruned it.
Related errors
- SUBTASK_NOT_FOUND
- projectRoot is required in args to resolve project paths
- MCP provider requires session object
- The MCP model function cannot be called with the new keyword
- MCP session must have client sampling capabilities
AI-assisted analysis of eyaltoledano/claude-task-master@c0c98d367c (2026-08-29).
Data as JSON: /api/errors/314766b75db4e759.
Report an issue: GitHub.