n8n-io/n8n · warning · NotFoundError

Workflow ${workflowId} does not exist.

Error message

Workflow ${workflowId} does not exist.

What it means

Thrown in the WorkflowStatisticsController middleware (the workflow-existence/permissions gate) when workflowFinderService.findWorkflowForUser(workflowId,user,['workflow:read']) returns a falsy result. The 404 deliberately conflates 'missing' and 'no-permission' to avoid leaking existence; the warn log captures the real reason as 'User attempted to read a workflow without permissions'. HTTP 404.

Source

Thrown at packages/cli/src/controllers/workflow-statistics.controller.ts:49

	// TODO: move this into a new decorator `@ValidateWorkflowPermission`
	@Middleware()
	async hasWorkflowAccess(req: StatisticsRequest.GetOne, _res: Response, next: NextFunction) {
		const { user } = req;
		const workflowId = req.params.id;

		const workflow = await this.workflowFinderService.findWorkflowForUser(workflowId, user, [
			'workflow:read',
		]);

		if (workflow) {
			next();
		} else {
			this.logger.warn('User attempted to read a workflow without permissions', {
				workflowId,
				userId: user.id,
			});
			// Otherwise, make and return an error
			throw new NotFoundError(`Workflow ${workflowId} does not exist.`);
		}
	}

	@Get('/:id/counts/')
	async getCounts(req: StatisticsRequest.GetOne): Promise<WorkflowStatisticsData<number>> {
		return await this.getData(req.params.id, 'count', 0);
	}

	@Get('/:id/times/')
	async getTimes(req: StatisticsRequest.GetOne): Promise<WorkflowStatisticsData<Date | null>> {
		return await this.getData(req.params.id, 'latestEvent', null);
	}

	@Get('/:id/data-loaded/')
	async getDataLoaded(req: StatisticsRequest.GetOne): Promise<IWorkflowStatisticsDataLoaded> {
		// Get flag
		const workflowId = req.params.id;

View on GitHub (pinned to 5ac6606e81)

Solutions

  1. Verify the workflow exists and is shared with the user's project (workflow:read scope) via the workflows API.
  2. If the workflow was deleted, stop polling statistics for it.
  3. Request the workflow:read permission on the owning project if access is intended.
Defensive patterns

Strategy: validation

Validate before calling

async function canReadWorkflow(workflowId: string) {
  const r = await fetch(`/rest/workflows/${workflowId}`);
  return r.ok; // 403/404 both surface as false
}
if (!(await canReadWorkflow(id))) {
  throw new Error('Workflow missing or not accessible (workflow:read required)');
}

Try / catch

try { await fetch(`/rest/workflow-statistics/${id}/counts`); }
catch (e) { if (e.statusCode === 404) { /* gone or no access; stop polling */ } else throw e; }

Prevention

When it happens

Trigger: Calling GET /workflow-statistics/:id/counts or /times where :id does not exist OR exists but is not accessible to the user (no workflow:read on the owning project). Same applies to any route that runs this middleware.

Common situations: User switched projects/tenants and the cached workflow id is now inaccessible; workflow was deleted; id copied from another instance; permission scope missing after an RBAC migration.

Related errors


AI-assisted analysis of n8n-io/n8n@5ac6606e81 (2026-08-12). Data as JSON: /api/errors/a4eb232c129f7532. Report an issue: GitHub.