Login crashes on a missing has_one relation (inventory/skills/location/music) #53

Closed
opened 2026-06-28 15:14:17 +00:00 by sickday · 0 comments
Owner

Problem

Game.Entity.Player init hydrates a profile's has_one relations directly — profile.inventory.backpack, profile.inventory.equipment, profile.skills, Location.to_tile(profile.location), profile.music. If any relation row is missing (nil), init raises and the player can't log in (the GenServer crash-loops). New-account registration (Game.World.Agent.create_new_profile/1) always creates all five rows, so this is a data-corruption / partial-migration tail risk, not a normal-path bug — but when it fires it's a hard login failure for the affected account.

This is a different bug class from #48 (truthy-empty || default): a nil relation is falsy and crashes loudly, whereas #48 covered present-but-empty structures slipping through a ||. The appearance relation is already guarded (usable_appearance/1Appearance.default/0 from #48); the other four are not.

Task

Guard each hydrated relation at login with a safe fallback, or fail the login gracefully with a logged error rather than crash-looping. Decide per-relation: default-row reconstruction (preferred where a sane default exists, e.g. skills/inventory) vs. clean login rejection.

Acceptance

  • A registered profile with any single missing relation row either logs in (with a sane default) or is rejected with a clear logged reason — never crash-loops.
  • Regression test per relation.

Refs: lib/black_lobster/game/entity/player.ex init hydration (~lines 653–765).

Part of the Security & Data-Integrity Hardening epic.

## Problem `Game.Entity.Player` init hydrates a profile's `has_one` relations directly — `profile.inventory.backpack`, `profile.inventory.equipment`, `profile.skills`, `Location.to_tile(profile.location)`, `profile.music`. If any relation row is missing (nil), init raises and the player can't log in (the GenServer crash-loops). New-account registration (`Game.World.Agent.create_new_profile/1`) always creates all five rows, so this is a **data-corruption / partial-migration tail risk**, not a normal-path bug — but when it fires it's a hard login failure for the affected account. This is a **different bug class from #48** (truthy-empty `|| default`): a nil relation is falsy and crashes loudly, whereas #48 covered present-but-empty structures slipping through a `||`. The appearance relation is already guarded (`usable_appearance/1` → `Appearance.default/0` from #48); the other four are not. ## Task Guard each hydrated relation at login with a safe fallback, or fail the login gracefully with a logged error rather than crash-looping. Decide per-relation: default-row reconstruction (preferred where a sane default exists, e.g. skills/inventory) vs. clean login rejection. ## Acceptance - A registered profile with any single missing relation row either logs in (with a sane default) or is rejected with a clear logged reason — never crash-loops. - Regression test per relation. Refs: `lib/black_lobster/game/entity/player.ex` init hydration (~lines 653–765). Part of the Security & Data-Integrity Hardening epic.
sickday self-assigned this 2026-06-28 15:14:42 +00:00
sickday added this to the v1 milestone 2026-06-28 15:14:54 +00:00
sickday added this to the v1 project 2026-06-28 15:14:56 +00:00
Sign in to join this conversation.
No milestone
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
BlackLobster/Server#53
No description provided.