n8n-io/n8n · error · Error

Cannot query across ${relation.relationType} for property ${

Error message

Cannot query across ${relation.relationType} for property ${path}

What it means

Thrown in QueryBuilder.createPropertyPath when, while expanding an entity into WHERE conditions, the code encounters a relation of type one-to-many or many-to-many on entity[key]. TypeORM cannot turn a to-many collection into a scalar WHERE predicate without an explicit subquery, so it aborts rather than emit a wrong query.

Source

Thrown at packages/@n8n/typeorm/src/query-builder/QueryBuilder.ts:1229

				// so if the join columns are all defined we can return just the relation itself
				// because it will fetch only the join columns and do the lookup.
				if (relation.relationType === 'one-to-one' || relation.relationType === 'many-to-one') {
					const joinColumns = relation.joinColumns
						.map((j) => j.referencedColumn)
						.filter((j): j is ColumnMetadata => !!j);

					const hasAllJoinColumns =
						joinColumns.length > 0 &&
						joinColumns.every((column) => column.getEntityValue(entity[key], false));

					if (hasAllJoinColumns) {
						paths.push(path);
						continue;
					}
				}

				if (relation.relationType === 'one-to-many' || relation.relationType === 'many-to-many') {
					throw new Error(`Cannot query across ${relation.relationType} for property ${path}`);
				}

				// For any other case, if the `entity[key]` contains all of the primary keys we can do a
				// lookup via these.  We don't need to look up via any other values 'cause these are
				// the unique primary keys.
				// This handles the situation where someone passes the model & we don't need to make
				// a HUGE where.
				const primaryColumns = relation.inverseEntityMetadata.primaryColumns;
				const hasAllPrimaryKeys =
					primaryColumns.length > 0 &&
					primaryColumns.every((column) => column.getEntityValue(entity[key], false));

				if (hasAllPrimaryKeys) {
					const subPaths = primaryColumns.map((column) => `${path}.${column.propertyPath}`);
					paths.push(...subPaths);
					continue;
				}

View on GitHub (pinned to 5ac6606e81)

Solutions

  1. Do not pass to-many collections in where conditions. Filter by the owning side's FK or by an inverse id: where: { id: In(comments.map(c => c.userId)) }.
  2. Use a subquery: qb.where(qb => qb.subQuery().select().from(Comment,'c').where('c.userId = user.id').getQuery()).
  3. Strip relation arrays from the entity before using it as a where object, or pass only the primary-key map.

Example fix

// before
await repo.find({ where: { comments: post.comments } }); // comments is one-to-many
// after
await repo.find({ where: { id: In(post.comments.map(c => c.postId)) } });
Defensive patterns

Strategy: validation

Validate before calling

function stripToMany(entity: Record<string, unknown>, meta: any) {
  const out: Record<string, unknown> = {};
  for (const [k, v] of Object.entries(entity)) {
    const rel = meta.relations.find((r: any) => r.propertyName === k);
    const isCollection = rel && (rel.relationType === 'one-to-many' || rel.relationType === 'many-to-many');
    if (!isCollection) out[k] = v;
  }
  return out;
}
// qb.where(meta.targetName, stripToMany(user, meta))

Type guard

function isToManyCollection(value: unknown, meta: any, key: string): boolean {
  const rel = meta.relations.find((r: any) => r.propertyName === key);
  return !!rel && (rel.relationType === 'one-to-many' || rel.relationType === 'many-to-many') && Array.isArray(value);
}

Prevention

When it happens

Trigger: Passing an entity instance into qb.where(Entity, { id }) (the object form) where the object has a populated to-many relation array, e.g. User.role entities passed as User with user.roles = [...]. Also when using repository.find({ where: { comments: [...] } }) with a one-to-many side.

Common situations: Developer loads an aggregate with relations via find({relations:true}) then reuses the hydrated object as a FindOptionsWhere; serializing a request body that nests a to-many array and feeding it straight into a where clause.

Related errors


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