---
title: Fix the unawaited getLeague call in the register handler
type: task
status: completed
description: >-
  api/routes/auth.mjs calls the async getLeague without awaiting it, so both league-validation
  branches in registration are unreachable: a nonexistent leagueId is accepted, and a full league is
  accepted. Found during an unrelated plan review.
base_uri: user:task/league/fix-unawaited-get-league-in-register.md
created_at: '2026-09-12T15:07:01.764Z'
entity_id: 41016dfe-7597-4673-ba9d-6b38af663f1f
finished_at: '2026-09-12T18:22:43.312Z'
observations:
  - ████████████████████████████████████████████████
  - ████████████████████████████████████████████████
  - ████████████████████████████████████████████████
  - ████████████████████████████████████████████████
owner_identity_uri: █████████████████████████
priority: Medium
public_read: true
relations:
  - relates [[██████████████████████-███████-████████-████-██████-███████████]]
tags:
  - user:tag/league-xo-football.md
updated_at: '2026-09-12T18:22:44.798Z'
visibility_analyzed_at: '2026-11-05T23:49:30.307Z'
---

# Fix the unawaited getLeague call in the register handler

## Overview

- `api/routes/auth.mjs` calls `getLeague` without awaiting it, so both league-validation branches in registration are dead code.
- Impact: a registration naming a nonexistent league is accepted instead of refused, and a registration into a league whose teams are already full is accepted instead of refused.

## Background

`libs-server/get-league.mjs:112` is `export default async function ({ lid, year })`. `api/routes/auth.mjs:301` calls it as `const league = getLeague({ lid: league_id })` with no `await`.

Two consequences follow, both observed by reading the handler:

- `league` is always a pending Promise, which is truthy, so `if (!league)` at `:302` can never fire and `league does not exist` at `:303` is unreachable.
- `league.number_teams` at `:314` is `undefined`, so `teams.length === league.number_teams` is never true and `league full` at `:315` is unreachable. An independent review corrected this reference from `:315`, which is the return rather than the comparison.

This task lands AFTER [[██████████████████████-███████-████████-████-██████-███████████]], which restructures `test/auth.spec.mjs` — the same file the specs below go into. The two code edits do not conflict: that task's transaction boundary opens below `:301` and leaves the league and team reads outside and before. Taking the pre-fix baseline after it lands means taking it with the window ceiling live, so raise the ceiling via the `account_creation_policy` config row in the temporary specs' `before` hook or the baseline reads 400 for the wrong reason.

Found during the plan review for [[██████████████████████-███████-████████-████-██████-███████████]]. It is deliberately NOT fixed there: that task rewrites the insert block a few lines below, and folding an unrelated behavior change into a schema-coupled commit is the thing the repository's one-commit DDL rule makes expensive to unpick.

### Assumptions

- No caller depends on the current broken behavior — unchecked. A registration flow that silently accepts a bad `leagueId` may have masked bad client input; check what the resulting `users_teams` rows look like before assuming the fix is purely additive.
- The two branches are genuinely unreachable rather than merely suspicious — checked by reading the function signature and the call site together. An executable confirmation is the first plan item, because structure establishes possibility and only observation establishes actuality.

## Plan

- [ ] **Confirm both branches are unreachable before changing anything**
  - File: `api/routes/auth.mjs`
  - Purpose: prove the defect occurs rather than inferring it from the missing keyword.
  - Changes: add a temporary spec registering with a `leagueId` that does not exist, and another into a full league.
  - Verify: both currently return 200; after the fix they must return 400 with `league does not exist` and `league full`. Run the pair before and after and require the readings to differ.

- [ ] **Await the call**
  - File: `api/routes/auth.mjs`
  - Purpose: make both guards reachable.
  - Changes: `await getLeague(...)`.
  - Verify: the specs above pass, and `yarn test:isolated test/auth.spec.mjs` prints a summary line with no regression.

- [ ] **Sweep for the same shape elsewhere**
  - Purpose: an unawaited async call is invisible to tests that only assert the happy path, so the one found is unlikely to be the only one.
  - Changes: grep every call site of the default exports in `libs-server/` that are `async` and check each for a missing `await`.
  - Verify: report the count of call sites checked and list any others found. A clean sweep must name how many sites it examined, or it is indistinguishable from a grep that could not match.
