← Back to game
Odds Graph Debug — 0042500156
SAS @ POR · 2026-04-30T04:00:00Z
·
price-history JSON ↗
·
alignment-debug JSON ↗
Summary
| game_row_found | True |
| playoff_row_found | True |
| game_time_utc | 2026-04-30T04:00:00Z |
| game_status_id | 1 |
| game_has_final_score | False |
| final_signal | none |
| game_start_ms | 1777521600000 2026-04-30T04:00:00Z |
| window_start_ms | 1777518000000 2026-04-30T03:00:00Z |
| basketball_final_ms | null |
| window_end_ms | null |
| window_duration_min | — |
| pg_final_ms_fallback used | False |
| odds_start_fallback used | False |
game_time_utc Refetch Execution
| gtu_refetch_attempted | False ← refetch was NOT triggered on this request |
| gtu_refetch_trigger_reason | game_time_utc already set |
| game_time_utc_before | |
| game_date (used for ScoreboardV3 call) | NULL |
| source_1_scoreboard_attempted | |
| source_1_scoreboard_found_game | |
| source_1_scoreboard_gameTimeUTC | null |
| source_2_boxscore_attempted | |
| source_2_boxscore_gameTimeUTC | null |
| gtu_refetch_source_used | none |
| gtu_refetch_value | null |
| db_path | — |
| rows_affected (SQL UPDATE) | |
| gtu_refetch_update_applied | |
| game_time_utc_after (re-read same conn) | null |
Source Rows
A. games table
| game_id | 0042500156 |
| game_status_id | 1 |
| game_time_utc | 2026-04-30T04:00:00Z |
| home_abbr | POR |
| visitor_abbr | SAS |
| home_pts | NULL |
| visitor_pts | NULL |
B. playoff_games table
| game_start_ms | 1777521600000 |
| scheduled_start_ms | 1777521600000 |
| basketball_final_ms | NULL |
| basketball_final_source | NULL |
| basketball_final_event_text | NULL |
| basketball_final_period | NULL |
| basketball_final_clock | NULL |
| basketball_final_raw_timestamp | NULL |
| home_team_abbr | POR |
| away_team_abbr | SAS |
| home_score | NULL |
| away_score | NULL |
| status | scheduled |
C. playoff_game_pbp_events diagnostics
| row count | 0 |
Games Row Provenance
Most Likely Write Source
confidence: High
fetch_and_store_games
game_time_utc is populated. This field is ONLY written by fetch_and_store_games() (ScoreboardV3). All other write paths omit it from their INSERT/UPDATE.
| this path writes game_time_utc | True |
| games.updated_at | — |
| games.game_time_utc (current) | 2026-04-30T04:00:00Z |
| games.game_status_id (current) | 1 |
| games.home_pts / visitor_pts | null / null |
Field-Level Write Coverage
| Field | Written by | NOT written by | Strategy / Risk |
|---|---|---|---|
| game_time_utc | fetch_and_store_games | sync_playoff_games, fetch_and_store_historical_game, copy_game_to_nba_db, boxscores_live_update, playoff_resolve_update |
COALESCE — set once on first non-null insert; never overwritten after that
Risk: High: only fetch_and_store_games writes it, and it only runs for today/yesterday |
| game_status_id | fetch_and_store_games, sync_playoff_games, fetch_and_store_historical_game, copy_game_to_nba_db, boxscores_live_update | playoff_resolve_update |
MAX(existing, new) in fetch_and_store_games; direct set in sync/historical/live
Risk: Low: all major paths write it |
| home_pts / visitor_pts | fetch_and_store_games, sync_playoff_games, fetch_and_store_historical_game, copy_game_to_nba_db, boxscores_live_update | playoff_resolve_update |
Varies: MAX for final (sync), COALESCE for live, direct for historical
Risk: Low: most paths populate scores |
| game_date | fetch_and_store_games, sync_playoff_games, fetch_and_store_historical_game, copy_game_to_nba_db | boxscores_live_update, playoff_resolve_update |
Set on INSERT; ON CONFLICT generally does not update it
Risk: Low: all INSERT paths include game_date |
All Candidate Write Paths
| Function | File | game_time_utc | can null gtu | old games | When / Note |
|---|---|---|---|---|---|
| fetch_and_store_games(game_date) likely | app/services/games.py | YES | Only if ScoreboardV3 returns gameTimeUTC=null (rare) | False |
Homepage load (today/yesterday), scheduler daily, startup warmup_games_db()
Warmup covers today+yesterday only. Games ≥2 days old are never re-fetched by this path.
|
| sync_playoff_games(season) | app/services/games.py | NO | True | True |
Rate-limited: called by playoff_analytics._sync_playoff_games_if_due() on bracket page fetch
LeagueGameFinder has no gameTimeUTC field. Creates complete rows (date, teams, final score) but leaves game_time_utc=NULL.
|
| fetch_and_store_historical_game(game_id) | app/services/historical.py | NO | True | True |
On-demand: /games/{game_id} visited and game not yet in DB
Bypassed if game already exists (sync_playoff_games may have created it). Does not populate game_time_utc.
|
| copy_game_to_nba_db(game_id) | app/services/playoff_history_db.py | NO | True | True |
Called first inside fetch_and_store_historical_game
Short-circuits the nba_api call if game is in the local history DB.
|
| fetch_and_store_live_boxscore() / store_live_period_scores() | app/services/boxscores.py | NO | False | False |
During live game polling (~every 5 s while game is live)
Never creates rows. Cannot set game_time_utc to NULL.
|
| _resolve_playoff_placeholders() | app/main.py | NO | False | False |
Homepage load when visitor/home team is still TBD
Only sets team identification fields. Cannot affect game_time_utc.
|
Execution Trace
| # | Step | Branch / Path | Output | Inputs & Fields | ||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 1 | Load games table row | found | found=True |
|
||||||||||||||||||||||||||||||||||||||||||
| 2 | Load playoff_games row | found | found=True |
|
||||||||||||||||||||||||||||||||||||||||||
| 3 | Load odds aggregate (playoff_game_odds_history) | no_data | cnt=0 min_ts=None max_ts=None |
|
||||||||||||||||||||||||||||||||||||||||||
| 4 | Derive scheduled_start_ms | games.game_time_utc | 1777521600000 (2026-04-30T04:00:00Z) |
|
||||||||||||||||||||||||||||||||||||||||||
| 5 | Derive game_start_ms | playoff_games.game_start_ms (actual) | 1777521600000 (2026-04-30T04:00:00Z) |
|
||||||||||||||||||||||||||||||||||||||||||
| 5b | Midnight-ET placeholder detection & start correction | midnight_et_no_bfr |
uncorrected → 1777521600000 (2026-04-30T04:00:00Z)
midnight-ET detected but no bfr_ms — cannot correct
|
|
||||||||||||||||||||||||||||||||||||||||||
| 6 | Detect game_has_final_score | none |
game_has_final_score=False final_signal=none
no scores in games row; pg.status ≠ 'final'; pg.basketball_final_ms is null
|
|
||||||||||||||||||||||||||||||||||||||||||
| 7 | Derive basketball_final_ms | not_called (game_has_final_score=False) |
null
bfp returned null; pg_row bfr_ms and basketball_final_ms also null
|
|
||||||||||||||||||||||||||||||||||||||||||
| 8 | Derive window_start_ms (game_start − 1 h) | game_start_minus_1h | 1777518000000 (2026-04-30T03:00:00Z) |
|
||||||||||||||||||||||||||||||||||||||||||
| 9 | Derive window_end_ms (basketball_final + 15 min) | null_no_basketball_final |
null
NULL: basketball_final_ms is null
|
|
Branch Decisions
scores_present
False
game_time_utc_present
True
past_date_in_et
False
status_id_is_3
False
pg_status_is_final
False
pg_status_is_in_progress
False
pg_has_basketball_final_ms
False
pg_bfm_suppressed_in_progress
False
scores_past_date_fired
False
scores_status3_sameday_fired
False
scores_status3_no_tip_fired
False
playoff_games_status_final_fired
False
playoff_games_has_final_ms_fired
False
midnight_et_start_detected
True
midnight_et_start_corrected
False
bfp_called
False
pg_final_ms_fallback_used
False
odds_start_fallback_used
False
Odds Filtering Breakdown
window_end_ms null — end boundary not applied
| odds source | — |
| raw rows total | 0 |
| rows before window_start_ms | 0 |
| rows in window (start → end) | 0 |
| rows after window_end_ms | 0 (window_end_ms null — filter not applied) |
| earliest timestamp (epoch s) | — |
| latest timestamp (epoch s) | — |
| local odds history rows | 0 |
| window_start_ms applied | 1777518000000 2026-04-30T03:00:00Z |
| window_end_ms applied | null |
Enrichment Trace
A. Referees
| query | game_referee_assignments JOIN referees |
| game_referee_assignments rows | 0 ← no assignments for this game_id → referee display shows "Not available" |
| referees after JOIN | 0 |
| note | No rows in game_referee_assignments for this game_id |
B. Lineup Marker
| query | lineup_state WHERE game_id = ? |
| lineup_state row found | False ← no row → marker cannot be drawn |
| note | No row in lineup_state for this game_id |
| final lineup_marker_ts | null |
| lineup_confirmed | — |
Raw Debug JSON (machine-readable)
Show full debug payload ↓
{
"game_id": "0042500156",
"gtu_refetched": false,
"gtu_refetch_trace": {
"attempted": false,
"trigger_reason": "game_time_utc already set"
},
"games_raw": {
"game_id": "0042500156",
"game_status_id": 1,
"game_time_utc": "2026-04-30T04:00:00Z",
"home_abbr": "POR",
"visitor_abbr": "SAS",
"home_pts": null,
"visitor_pts": null
},
"playoff_games_raw": {
"game_start_ms": 1777521600000,
"scheduled_start_ms": 1777521600000,
"basketball_final_ms": null,
"basketball_final_source": null,
"basketball_final_event_text": null,
"basketball_final_period": null,
"basketball_final_clock": null,
"basketball_final_raw_timestamp": null,
"home_team_abbr": "POR",
"away_team_abbr": "SAS",
"home_score": null,
"away_score": null,
"status": "scheduled"
},
"ph_agg": {
"cnt": 0,
"min_ts": null,
"max_ts": null
},
"bfp": {},
"bfp_called": false,
"bfp_error": null,
"final_branches": {
"scores_present": false,
"game_time_utc_present": true,
"past_date_in_et": false,
"status_id_is_3": false,
"pg_status_is_final": false,
"pg_status_is_in_progress": false,
"pg_has_basketball_final_ms": false,
"pg_bfm_suppressed_in_progress": false,
"scores_past_date_fired": false,
"scores_status3_sameday_fired": false,
"scores_status3_no_tip_fired": false,
"playoff_games_status_final_fired": false,
"playoff_games_has_final_ms_fired": false,
"midnight_et_start_detected": true,
"midnight_et_start_corrected": false
},
"window": {
"game_start_ms": 1777521600000,
"window_start_ms": 1777518000000,
"window_end_ms": null,
"basketball_final_ms": null,
"window_duration_min": null
},
"odds_filter": {
"raw_count": 0,
"before_window": 0,
"in_window": 0,
"after_window": 0,
"uncategorized": 0
},
"referee_trace": {
"query": "game_referee_assignments JOIN referees",
"assignments_found": 0,
"referees_found": 0,
"error": null,
"note": "No rows in game_referee_assignments for this game_id"
},
"lineup_trace": {
"query": "lineup_state WHERE game_id = ?",
"row_found": false,
"is_confirmed": null,
"telegram_first_detected_at": null,
"first_confirmed_at": null,
"chosen_field": null,
"error": null,
"note": "No row in lineup_state for this game_id"
},
"pbp_row_count": 0,
"pbp_last_events": [],
"games_write_paths": [
{
"id": "fetch_and_store_games",
"file": "app/services/games.py",
"function": "fetch_and_store_games(game_date)",
"when": "Homepage load (today/yesterday), scheduler daily, startup warmup_games_db()",
"source_api": "ScoreboardV3 (nba_api)",
"writes_game_time_utc": true,
"game_time_utc_strategy": "COALESCE(existing, new) \u2014 fills if null, never overwrites non-null",
"can_create_null_gtu": "Only if ScoreboardV3 returns gameTimeUTC=null (rare)",
"covers_old_games": false,
"note": "Warmup covers today+yesterday only. Games \u22652 days old are never re-fetched by this path."
},
{
"id": "sync_playoff_games",
"file": "app/services/games.py",
"function": "sync_playoff_games(season)",
"when": "Rate-limited: called by playoff_analytics._sync_playoff_games_if_due() on bracket page fetch",
"source_api": "LeagueGameFinder (nba_api)",
"writes_game_time_utc": false,
"game_time_utc_strategy": "NOT in INSERT column list; NOT in ON CONFLICT SET \u2014 always NULL for new rows",
"can_create_null_gtu": true,
"covers_old_games": true,
"note": "LeagueGameFinder has no gameTimeUTC field. Creates complete rows (date, teams, final score) but leaves game_time_utc=NULL."
},
{
"id": "fetch_and_store_historical_game",
"file": "app/services/historical.py",
"function": "fetch_and_store_historical_game(game_id)",
"when": "On-demand: /games/{game_id} visited and game not yet in DB",
"source_api": "BoxScoreSummaryV3 (nba_api) / playoff_history_db",
"writes_game_time_utc": false,
"game_time_utc_strategy": "NOT in INSERT column list; NOT in ON CONFLICT SET",
"can_create_null_gtu": true,
"covers_old_games": true,
"note": "Bypassed if game already exists (sync_playoff_games may have created it). Does not populate game_time_utc."
},
{
"id": "copy_game_to_nba_db",
"file": "app/services/playoff_history_db.py",
"function": "copy_game_to_nba_db(game_id)",
"when": "Called first inside fetch_and_store_historical_game",
"source_api": "Local playoff_history.db SQLite file",
"writes_game_time_utc": false,
"game_time_utc_strategy": "NOT in INSERT column list; NOT in ON CONFLICT SET",
"can_create_null_gtu": true,
"covers_old_games": true,
"note": "Short-circuits the nba_api call if game is in the local history DB."
},
{
"id": "boxscores_live_update",
"file": "app/services/boxscores.py",
"function": "fetch_and_store_live_boxscore() / store_live_period_scores()",
"when": "During live game polling (~every 5 s while game is live)",
"source_api": "data.nba.com live BoxScore",
"writes_game_time_utc": false,
"game_time_utc_strategy": "UPDATE only: game_status_id, status_text, home_pts, visitor_pts",
"can_create_null_gtu": false,
"covers_old_games": false,
"note": "Never creates rows. Cannot set game_time_utc to NULL."
},
{
"id": "playoff_resolve_update",
"file": "app/main.py",
"function": "_resolve_playoff_placeholders()",
"when": "Homepage load when visitor/home team is still TBD",
"source_api": "postseason_state / standings (DB-only)",
"writes_game_time_utc": false,
"game_time_utc_strategy": "UPDATE only: visitor/home team abbr, name, team_id",
"can_create_null_gtu": false,
"covers_old_games": false,
"note": "Only sets team identification fields. Cannot affect game_time_utc."
}
],
"likely_games_write_source": {
"source": "fetch_and_store_games",
"reason": "game_time_utc is populated. This field is ONLY written by fetch_and_store_games() (ScoreboardV3). All other write paths omit it from their INSERT/UPDATE.",
"confidence": "High",
"writes_game_time_utc": true
},
"field_write_coverage": {
"game_time_utc": {
"written_by": [
"fetch_and_store_games"
],
"not_written_by": [
"sync_playoff_games",
"fetch_and_store_historical_game",
"copy_game_to_nba_db",
"boxscores_live_update",
"playoff_resolve_update"
],
"strategy": "COALESCE \u2014 set once on first non-null insert; never overwritten after that",
"null_risk": "High: only fetch_and_store_games writes it, and it only runs for today/yesterday"
},
"game_status_id": {
"written_by": [
"fetch_and_store_games",
"sync_playoff_games",
"fetch_and_store_historical_game",
"copy_game_to_nba_db",
"boxscores_live_update"
],
"not_written_by": [
"playoff_resolve_update"
],
"strategy": "MAX(existing, new) in fetch_and_store_games; direct set in sync/historical/live",
"null_risk": "Low: all major paths write it"
},
"home_pts / visitor_pts": {
"written_by": [
"fetch_and_store_games",
"sync_playoff_games",
"fetch_and_store_historical_game",
"copy_game_to_nba_db",
"boxscores_live_update"
],
"not_written_by": [
"playoff_resolve_update"
],
"strategy": "Varies: MAX for final (sync), COALESCE for live, direct for historical",
"null_risk": "Low: most paths populate scores"
},
"game_date": {
"written_by": [
"fetch_and_store_games",
"sync_playoff_games",
"fetch_and_store_historical_game",
"copy_game_to_nba_db"
],
"not_written_by": [
"boxscores_live_update",
"playoff_resolve_update"
],
"strategy": "Set on INSERT; ON CONFLICT generally does not update it",
"null_risk": "Low: all INSERT paths include game_date"
}
}
}