How It Works Features Pricing Blog
Log In Start Free Trial
Flask · Python

Fix KeyError: KeyError: 'user_id' in Flask

This error occurs when accessing a dictionary key that does not exist in the Flask request data, typically using request.form['user_id'] or request.json['user_id'] when the key is missing. Fix it by using request.form.get() or request.json.get() with a default value, and validate required fields explicitly.

Reading the Stack Trace

Traceback (most recent call last): File "/app/venv/lib/python3.11/site-packages/flask/app.py", line 1498, in __call__ return self.wsgi_app(environ, start_response) File "/app/venv/lib/python3.11/site-packages/flask/app.py", line 1476, in wsgi_app response = self.handle_exception(e) File "/app/venv/lib/python3.11/site-packages/flask/app.py", line 1473, in wsgi_app response = self.full_dispatch_request() File "/app/venv/lib/python3.11/site-packages/flask/app.py", line 882, in full_dispatch_request rv = self.dispatch_request() File "/app/venv/lib/python3.11/site-packages/flask/app.py", line 868, in dispatch_request return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args) File "/app/src/routes.py", line 15, in update_user user_id = request.json['user_id'] KeyError: 'user_id'

Here's what each line means:

Common Causes

1. Missing required field in request body

The client sends a JSON payload without the 'user_id' field, and the server accesses it with bracket notation which raises KeyError.

@app.route('/api/users', methods=['PUT'])
def update_user():
    user_id = request.json['user_id']
    name = request.json['name']
    user = User.query.get(user_id)
    user.name = name
    db.session.commit()
    return jsonify({'status': 'updated'})

2. Wrong Content-Type header

The client sends data as form-encoded but the server reads request.json, which is None when Content-Type is not application/json.

@app.route('/api/users', methods=['PUT'])
def update_user():
    user_id = request.json['user_id']  # request.json is None if Content-Type != application/json
    return jsonify({'user_id': user_id})

The Fix

Use request.get_json() and .get() to safely access fields without raising KeyError. Validate required fields explicitly and return proper 400 responses when they are missing.

Before (broken)
@app.route('/api/users', methods=['PUT'])
def update_user():
    user_id = request.json['user_id']
    name = request.json['name']
    user = User.query.get(user_id)
    user.name = name
    db.session.commit()
    return jsonify({'status': 'updated'})
After (fixed)
from flask import request, jsonify, abort

@app.route('/api/users', methods=['PUT'])
def update_user():
    data = request.get_json()
    if not data:
        abort(400, description='Request body must be JSON')

    user_id = data.get('user_id')
    name = data.get('name')

    if not user_id or not name:
        abort(400, description='user_id and name are required')

    user = User.query.get(user_id)
    if not user:
        abort(404, description='User not found')

    user.name = name
    db.session.commit()
    return jsonify({'status': 'updated'})

Testing the Fix

import pytest
from app import create_app, db
from models import User


@pytest.fixture
def app():
    app = create_app()
    app.config['TESTING'] = True
    app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///:memory:'
    with app.app_context():
        db.create_all()
        yield app
        db.drop_all()


@pytest.fixture
def client(app):
    return app.test_client()


@pytest.fixture
def sample_user(app):
    with app.app_context():
        user = User(id=1, name='Alice')
        db.session.add(user)
        db.session.commit()
        return user


def test_update_user_returns_400_when_body_is_empty(client):
    response = client.put('/api/users', content_type='application/json')
    assert response.status_code == 400


def test_update_user_returns_400_when_fields_missing(client):
    response = client.put('/api/users', json={'name': 'Bob'})
    assert response.status_code == 400


def test_update_user_succeeds_with_valid_data(client, sample_user):
    response = client.put('/api/users', json={'user_id': 1, 'name': 'Bob'})
    assert response.status_code == 200
    assert response.json['status'] == 'updated'

Run your tests:

pytest

Pushing Through CI/CD

git checkout -b fix/keyerror-request-validation,git add src/routes.py tests/test_routes.py,git commit -m "fix: validate request JSON fields before accessing them",git push origin fix/keyerror-request-validation

Your CI config should look something like this:

name: CI
on:
  pull_request:
    branches: [main]
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: '3.11'
      - run: pip install -r requirements.txt
      - run: pytest --tb=short
      - run: flake8 .

The Full Manual Process: 18 Steps

Here's every step you just went through to fix this one bug:

  1. Notice the error alert or see it in your monitoring tool
  2. Open the error dashboard and read the stack trace
  3. Identify the file and line number from the stack trace
  4. Open your IDE and navigate to the file
  5. Read the surrounding code to understand context
  6. Reproduce the error locally
  7. Identify the root cause
  8. Write the fix
  9. Run the test suite locally
  10. Fix any failing tests
  11. Write new tests covering the edge case
  12. Run the full test suite again
  13. Create a new git branch
  14. Commit and push your changes
  15. Open a pull request
  16. Wait for code review
  17. Merge and deploy to production
  18. Monitor production to confirm the error is resolved

Total time: 30-60 minutes. For one bug.

Or Let bugstack Fix It in Under 2 minutes

Every step above? bugstack does it automatically.

Step 1: Install the SDK

pip install bugstack

Step 2: Initialize

import bugstack

bugstack.init(api_key=os.environ["BUGSTACK_API_KEY"])

Step 3: There is no step 3.

bugstack handles everything from here:

  1. Captures the stack trace and request context
  2. Pulls the relevant source files from your GitHub repo
  3. Analyzes the error and understands the code context
  4. Generates a minimal, verified fix
  5. Runs your existing test suite
  6. Pushes through your CI/CD pipeline
  7. Deploys to production (or opens a PR for review)

Time from error to fix deployed: Under 2 minutes.

Human involvement: zero.

Try bugstack Free →

No credit card. 5-minute setup. Cancel anytime.

Deploying the Fix (Manual Path)

  1. Run pytest locally to confirm validation handles missing and present fields correctly.
  2. Open a pull request with the request validation changes.
  3. Wait for CI checks to pass on the PR.
  4. Have a teammate review the validation logic.
  5. Merge to main and verify the API returns proper error messages in staging.

Frequently Asked Questions

BugStack tests the endpoint with missing fields, empty bodies, and valid payloads. It verifies correct status codes and error messages for each case.

All fixes are delivered as pull requests. Your CI pipeline runs the test suite and your team reviews before merging.

For complex APIs, yes. Libraries like marshmallow or Flask-Pydantic provide schema-based validation that is more maintainable than manual checks.

request.json returns None when the Content-Type header is not application/json. Use request.get_json(force=True) to parse JSON regardless of Content-Type, or validate the header.