Nobody has started this yet — be first.
Business impact
A support rep or a user importing/entering contacts with a one-character name (an initial like "Q", "X", a single-letter nickname, or certain transliterated names) gets a 422 "name must not be blank" error on data that is very obviously not blank. This is exactly the kind of validation bug that generates confused support tickets ("your form is broken, I typed a name!") and erodes trust in the API's error messages -- the message promises one rule ("not blank") while the code enforces a stricter, undocumented one.
Problem
POST /api/contacts with {"name": "Q"} returns 422 Unprocessable Entity with {"error": "validation_error", "details": {"name": ["name must not be blank"]}}, even though "Q" is a non-blank string.
Current behavior
A one-character contact name is rejected with a 422 whose own error message ("must not be blank") does not match the rule actually being enforced.
Expected behavior
Any non-empty name (length >= 1) should be accepted. Only a genuinely blank/empty string should be rejected with the "name must not be blank" message.
Steps to reproduce
curl -s -X POST http://127.0.0.1:5000/api/contacts
-H 'Content-Type: application/json'
-d '{"name": "Q"}'
Why this matters
Marshmallow's validate.Length(min=...) boundary is the single source of truth for "how short can a name be" -- get the boundary wrong by one and you silently narrow the contract further than the error message (or anyone reading the code) would assume. This is the simplest possible instance of an off-by-one: a boundary check with a hardcoded number that no longer matches its own doc string.
Suggested approach
Look at ContactSchema.name in app/blueprints/contacts/schemas.py. Compare the validate.Length(min=...) argument against what the error= message on the same line claims the rule is. Fix the boundary so the code and the message agree, without weakening validation for genuinely blank input (an empty string "" must still be rejected).
Acceptance criteria
Verification
SECRET_KEY=dev-secret pytest practice_tickets/tests/test_ticket_01_name_length_boundary.py -v
Hints (0/2)
Try it without hints first — the reading is the exercise.
Working on this ticket
Work on a branch named for the ticket — that's what you'll submit.
Branch off your fork
$git checkout -b fix/single-char-name-rejectedFix it and commit
Meet every acceptance criterion, and add a test that would have caught this.
Push the branch
$git push -u origin fix/single-char-name-rejectedSubmit it below
Paste your fork URL and the branch name, with a short write-up of the root cause.
Questions
Ask about anything unclear in the ticket — the maintainer and anyone who has solved it can answer. Please don't post full solutions.
Sign in to ask a question or reply.
Sign inBy default DevelopmentConfig uses a local SQLite file, no Postgres server required -- point DATABASE_URL at a real Postgres instance instead if you want one, or use Docker Compose instead (cp .env.example .env then docker compose up --build, which starts a healthchecked Postgres 16 container plus the app served by gunicorn behind entrypoint.sh, which waits for Postgres, runs flask db upgrade, then starts gunicorn -- migrations are always applied on boot). The API is then available at http://localhost:5000 either way.
The existing suite runs under TestingConfig (an in-memory SQLite database created fresh per test, so it never touches your dev database):
source .venv/bin/activate
export SECRET_KEY=dev-secret
pytest -v
Work the tickets in practice_tickets/tickets/ (TICKET-01 through TICKET-07); each names one dedicated test in practice_tickets/tests/ -- a separate pytest package from the project's normal tests/, which plain pytest runs by default:
SECRET_KEY=dev-secret pytest practice_tickets/tests/test_ticket_01_name_length_boundary.py -v # a single ticket
./practice_tickets/run_tickets.sh # all 7, clean pass/fail summary
All 7 dedicated tests fail out of the box -- that is the starting point, not a setup mistake. Fixing TICKET-02 (search operator flip) and TICKET-04 (CSV row-number off-by-one) correctly also turns 4 currently-failing tests in the project's own tests/ suite back to green.
Level 1
Fix a bug
Read existing behaviour, correct it.