Last updated: April 25, 2026
Writing the article now based on the full brief.
By the end of this guide, you will have a working Django login page – no more cryptic error screens, no more guessing where your templates went. You will understand exactly why Django could not find your template, fix the configuration in under five minutes, and know how to prevent the same problem from happening again.
If you have ever stared at a TemplateDoesNotExist error and wondered why Django refuses to find a file that clearly exists on disk, you are not alone. This error trips up beginners and experienced developers alike. The good news: it always comes down to configuration, and the fix is precise.
Prerequisites

Image: Stack Overflow
Before following this guide, you should have:
- Django installed and a project created (
django-admin startproject) - Basic familiarity with Django’s project structure (
settings.py,urls.py, views) - A code editor and a terminal
- Python 3.10+ (the guide was written against Django 5.2 on Python 3.13, but applies to all recent versions)
Understanding the Django TemplateDoesNotExist Error
Django raises TemplateDoesNotExist the moment its template loader finishes searching every configured location and comes up empty. The error is not a bug – it is Django being explicit about what it looked for and where.
The most instructive part of the error page is the Template-loader postmortem section. Read it carefully. It tells you exactly which directories Django searched. If your project-level templates/ folder is not in that list, you have found the root cause.
Here is a typical postmortem output for a missing registration/login.html:
django.template.loaders.app_directories.Loader:
/path/to/venv/lib/python3.13/site-packages/django/contrib/admin/templates (Source does not exist)
/path/to/venv/lib/python3.13/site-packages/django/contrib/auth/templates (Source does not exist)
Notice what is missing: your own project’s templates/ directory never appears. Django only searched inside installed app packages – it never looked at your project root. That is the problem.
Common mistake: Many developers create a templates/ folder at the project root, put their template files inside it, and assume Django will find them automatically. It will not – unless you tell it to.
Why Django Cannot Find Your Template
The APP_DIRS: True setting only covers app-level templates. It does not scan your project root.
Django’s template resolution order is deterministic and worth memorising:
- It checks every directory listed in
TEMPLATES[0]['DIRS'] - It then checks the
templates/subdirectory of each installed app (whenAPP_DIRS: True)
If DIRS is an empty list – [] – Django skips step one entirely. Silently. Without warning. Any template you place in a project-level templates/ folder simply will not be found.
This is the exact scenario behind most TemplateDoesNotExist errors for auth views like LoginView. Django’s built-in LoginView looks for registration/login.html at a conventional path. Django provides the view – but it does not provide the template. You must create it. And for Django to find it, you must tell Django where to look.
How to Fix the Django TemplateDoesNotExist Error: Step-by-Step
Step 1 – Read the postmortem carefully
Open the error page in your browser. Scroll to the Template-loader postmortem section. Confirm that your project’s templates/ directory is absent from the list.
If you see only paths inside your virtual environment’s site-packages, your DIRS setting is the culprit.
Step 2 – Open settings.py
Find the TEMPLATES setting. It will look something like this:
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
The problem is on this line:
'DIRS': [],
An empty list tells Django: “Do not search anywhere outside of installed apps.”
Step 3 – Add your project-level templates directory
Replace the empty DIRS list with a reference to your project’s templates/ folder:
import os
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, 'templates')],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
BASE_DIR is defined near the top of settings.py and points to your project root. os.path.join(BASE_DIR, 'templates') constructs the full path to a templates/ directory at that root – cross-platform, no hard-coded slashes.
Step 4 – Create the template directory and file
Now create the directory structure Django expects:
your_project/
├── manage.py
├── your_project/
│ └── settings.py
└── templates/
└── registration/
└── login.html
Run these commands from your project root:
mkdir -p templates/registration
touch templates/registration/login.html
Step 5 – Write a minimal login template
Add a working login form to templates/registration/login.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Log in</title>
</head>
<body>
<h1>Log in</h1>
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Log in</button>
</form>
</body>
</html>
{% csrf_token %} is mandatory – Django will reject form submissions without it. {{ form.as_p }} renders all form fields automatically.
Step 6 – Verify your urls.py includes auth URLs
Check that your project-level urls.py includes Django’s auth URLs:
from django.urls import path, include
urlpatterns = [
path('accounts/', include('django.contrib.auth.urls')),
]
This registers LoginView at accounts/login/ and points it at registration/login.html by default.
Step 7 – Restart the development server and test
python manage.py runserver
Navigate to http://127.0.0.1:8000/accounts/login/. You should see your login form. The TemplateDoesNotExist error is gone.
Alternative Fix: App-Level Templates
There is a second valid approach. Instead of a project-level templates/ folder, place your templates inside an installed app.
If you have an app called accounts, create this structure:
accounts/
└── templates/
└── registration/
└── login.html
Because APP_DIRS: True is set, Django will discover this automatically – no changes to DIRS required. This approach suits teams who want templates co-located with the app logic that uses them. For free Django tutorials and further reading on project layout conventions, LearnDjango is an excellent starting point.
Before vs. after – at a glance:
| Before | After | |
|---|---|---|
DIRS |
[] |
[os.path.join(BASE_DIR, 'templates')] |
| Template loader postmortem | App dirs only | Project templates/ + app dirs |
registration/login.html found? |
No | Yes |
Nuances Most Developers Miss
If you see TemplateDoesNotExist but the postmortem does list your directory, the problem is the file path – not the directory. Django is case-sensitive on Linux and macOS. Registration/login.html and registration/login.html are different files. Check capitalisation.
A second subtle issue: APP_DIRS and a custom loaders key cannot coexist in the same TEMPLATES entry. If you have ever copied a snippet that sets loaders manually, remove it or remove APP_DIRS. Django will raise a ImproperlyConfigured error if you combine them – but not always immediately, which can be confusing.
Finally, this error pattern is not unique to auth views. Any view that renders a template by path – including class-based views with a template_name attribute – will produce the same error if the template loader cannot resolve the path. The fix is always the same: confirm your DIRS setting, confirm the file exists at the expected path, and read the postmortem.
Next Steps
Now that your login page is working, consider these logical next steps:
- Extend the auth flow – add
registration/logged_out.html,registration/password_reset_form.html, and the rest of the auth template set - Use template inheritance – create a
base.htmlwith your site’s shared layout and{% extends "base.html" %}in every child template - Explore Django class-based views –
LoginView,LogoutView, andPasswordChangeVieware all customisable viatemplate_nameandget_context_data - Look at a full-stack framework – if you are comparing Django to other options, our Laravel 12 Installation Guide shows how a PHP framework handles the same concerns
If you are building a production Django application and want expert help with architecture, deployment, or custom features, the team at drs-web.co.uk/contact is happy to help.
Frequently Asked Questions
Q: What causes the Django TemplateDoesNotExist error?
A: Django raises this error when its template loader finishes searching every configured location without finding the referenced template file. The most common cause is an empty DIRS list in TEMPLATES settings, which prevents Django from searching your project-level templates folder.
Q: Why does Django not find my template even though the file exists?
A: Django only searches directories you have explicitly listed in TEMPLATES[0]['DIRS'] and the templates/ subdirectories of installed apps. If your file is in a project-level templates/ folder but DIRS is empty, Django will never look there.
Q: Do I need to provide the login template myself?
A: Yes. Django provides LoginView but not the registration/login.html template it requires. You must create the template and ensure Django can find it via your DIRS or app-level templates configuration.
Q: What is the difference between DIRS and APP_DIRS in Django templates?
A: DIRS is an explicit list of directories Django searches first. APP_DIRS: True tells Django to also search the templates/ subdirectory inside each installed app. Both can be active simultaneously, and Django checks DIRS before APP_DIRS.
Q: Does this error only occur on Windows or specific Python versions?
A: No. This configuration issue occurs across all platforms and Django versions. The error has been reproduced on Windows with Django 5.2 and Python 3.13, but the same misconfiguration produces the same error on Linux and macOS with any recent Django version.
This article was researched and written with AI assistance, then reviewed for accuracy and quality. Kev Parker uses AI tools to help produce content faster while maintaining editorial standards.
Need help with your web project?
From one-day launches to full-scale builds, DRS Web Development delivers modern, fast websites.



