﻿id	summary	reporter	owner	description	type	status	component	version	severity	resolution	keywords	cc	stage	has_patch	needs_docs	needs_tests	needs_better_patch	easy	ui_ux
36480	FieldError when referencing a nonexistent alias provides less information than nonexistent annotation	Jacob Walls	Shubham Akhilesh Singh	"When migrating some querysets to use `.alias()` rather than `.annotate()` for [https://docs.djangoproject.com/en/5.2/ref/models/querysets/#django.db.models.query.QuerySet.alias performance purposes], I noticed when you typo a reference to an argument to `.alias()`, the hint provided by the `FieldError` does not include it.

(Whereas when using `.annotate()`, the `FieldError` hint ''does'' include what you meant.)

{{{#!py
In [1]: from django.db.models import F
In [2]: Tile.objects.alias(my_alias=F(""pk"")).order_by(""typo"")  # notice ""my_alias"" missing from hint
---------------------------------------------------------------------------
FieldError                                Traceback (most recent call last)
Cell In[2], line 1
----> 1 Tile.objects.alias(my_alias=F(""pk"")).order_by(""typo"")

File ~/py313/lib/python3.13/site-packages/django/db/models/query.py:1722, in QuerySet.order_by(self, *field_names)
   1720 obj = self._chain()
   1721 obj.query.clear_ordering(force=True, clear_default=False)
-> 1722 obj.query.add_ordering(*field_names)
   1723 return obj

File ~/py313/lib/python3.13/site-packages/django/db/models/sql/query.py:2291, in Query.add_ordering(self, *ordering)
   2288         continue
   2289     # names_to_path() validates the lookup. A descriptive
   2290     # FieldError will be raise if it's not.
-> 2291     self.names_to_path(item.split(LOOKUP_SEP), self.model._meta)
   2292 elif not hasattr(item, ""resolve_expression""):
   2293     errors.append(item)

File ~/py313/lib/python3.13/site-packages/django/db/models/sql/query.py:1805, in Query.names_to_path(self, names, opts, allow_many, fail_on_missing)
   1797     if pos == -1 or fail_on_missing:
   1798         available = sorted(
   1799             [
   1800                 *get_field_names_from_opts(opts),
   (...)
   1803             ]
   1804         )
-> 1805         raise FieldError(
   1806             ""Cannot resolve keyword '%s' into field. ""
   1807             ""Choices are: %s"" % (name, "", "".join(available))
   1808         )
   1809     break
   1810 # Check if we need any joins for concrete inheritance cases (the
   1811 # field lives in parent, but we are currently in one of its
   1812 # children)

FieldError: Cannot resolve keyword 'typo' into field. Choices are: child, data, file, geojsongeometry, nodegroup, nodegroup_id, parenttile, parenttile_id, provisionaledits, resourceinstance, resourceinstance_id, resxres, sortorder, tileid, vwannotation
}}}

----
I'm seeing this fixed with the following, but I stopped short of investigating the blame to see if it was explored before. (A contributor might have a look into that and report back?)
{{{#!diff
diff --git a/django/db/models/sql/query.py b/django/db/models/sql/query.py
index 20dbf7cfaa..3d9f39458b 100644
--- a/django/db/models/sql/query.py
+++ b/django/db/models/sql/query.py
@@ -1820,7 +1820,7 @@ class Query(BaseExpression):
                     available = sorted(
                         [
                             *get_field_names_from_opts(opts),
-                            *self.annotation_select,
+                            *self.annotations,
                             *self._filtered_relations,
                         ]
                     )
}}}

(For anybody who followed #36380, you'll be glad to hear `.alias()` let me prune my hundreds of useless annotations 😅)"	Cleanup/optimization	closed	Database layer (models, ORM)	dev	Normal	fixed	typo		Ready for checkin	1	0	0	0	0	0
