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

  1. Re-issue the original operation (start the long-running tool again) and use the freshly returned operation ID.
  2. Verify the operationId string exactly matches the one returned when the operation was started.
  3. 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

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


AI-assisted analysis of eyaltoledano/claude-task-master@c0c98d367c (2026-08-29). Data as JSON: /api/errors/314766b75db4e759. Report an issue: GitHub.