Surfaced by a fresh diagnose run on the Bizzymod Campaign server (2026-05-29). Both bugs are in the SourcePawn plugin, not the schema — the schema is doing its job (the FK constraint is what's catching bug B).
Bug A — query buffer truncation in bizzymod_stats.smx
addons/sourcemod/logs/errors_*.log is filling with MySQL errors where column names are being chopped mid-word:
[bizzymod-stats] transaction failed (q#0): Unknown column 'VALUE' in 'field list'
[bizzymod-stats] transaction failed (q#0): Unknown column 'VALU' in 'field list'
[bizzymod-stats] transaction failed (q#0): Unknown column 'V' in 'field list'
[bizzymod-stats] transaction failed (q#0): Unknown column 'damage_to_ta' in 'field list'
[bizzymod-stats] transaction failed (q#0): Unknown column 'damage_to_tan' in 'field list'
[bizzymod-stats] transaction failed (q#0): You have an error in your SQL syntax;
check the manual that corresponds to your MySQL server version for the right
syntax to use near '' at line 1
Diagnosis. The truncation pattern is the tell:
| Fragment |
Underlying name |
VALUE, VALU, V |
the SQL keyword VALUES getting clipped at various offsets |
damage_to_ta, damage_to_tan |
the damage_to_tank column in player_stats / tank_records |
empty '' syntax error |
the whole query string is being cut off mid-statement |
This is a buffer-size bug in query construction — a Format() or StrCat call where the destination is too small for the realistic column list. The same query is sometimes truncated at different lengths (V vs VALU vs VALUE), which suggests the size depends on a variable-length prefix (e.g. column-name list ordering, server identifier, map identifier).
Quick triage paths in the plugin source:
- Anywhere we build column lists for
INSERT INTO player_stats or INSERT INTO tank_records — these have the longest column counts (player_stats is ~50 columns after 007_extended_stats.sql).
- Look for
char query[NNN] / char buf[NNN] declarations where NNN < ~2048 near INSERT builders.
- The fact that even
VALUES is getting clipped means we're losing the closing half of the statement, so the buffer's running out partway through the column-list-then-VALUES build.
Suggested fix direction:
- Raise the query buffer to ≥ 4096 bytes for the wide-column inserts, or
- Switch to parameterized queries / prepared statements (avoids string-build entirely), or
- Use
DBI's transaction batching with one statement per query handle.
Option 2 is the right long-term answer — also closes a latent SQLi/escaping surface area.
Bug B — tank_records.fk_tr_map foreign-key race
Throughout the play session:
[bizzymod-stats] tank insert: Cannot add or update a child row: a foreign key
constraint fails (`bizzymod_stats`.`tank_records`,
CONSTRAINT `fk_tr_map` FOREIGN KEY (`map_id`)
REFERENCES `maps` (`id`) ON DELETE CASCADE ON UPDATE CASCADE)
Schema is from migrations/mysql/013_round_shaping_events.sql:
CREATE TABLE `tank_records` (
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
`server_id` INT UNSIGNED NOT NULL,
`map_id` INT UNSIGNED NOT NULL,
...
CONSTRAINT `fk_tr_map` FOREIGN KEY (`map_id`)
REFERENCES `maps` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
);
The plugin is inserting tank rows with a map_id that doesn't exist in maps at insert time. Two plausible causes:
- Map upsert hasn't completed yet — when a tank spawns on
c2m1_highway, the plugin records the spawn before (or concurrently with) the maps-table upsert for that map name. Async queue ordering.
- Cached stale map_id — plugin caches
map_id once per server lifetime; if maps is wiped or a migration renumbers IDs, the cache outlives the rows.
The errors are tightly clustered around map-transition timestamps in the log (03:41-03:50 was the last cluster, all on map c10m1_caves → c2m1_highway), which points strongly at hypothesis 1.
Suggested fix direction:
- Make the maps-row upsert synchronous before the first tank insert per map, or
- Have the tank insert path do
INSERT IGNORE INTO maps (name) VALUES (?) then SELECT id FROM maps WHERE name=? (or use INSERT ... ON DUPLICATE KEY UPDATE id=LAST_INSERT_ID(id) pattern) before chaining into tank_records insert.
Impact
- Data loss: tank records and (per Bug A) full player_stats writes for some sessions are silently dropping. Diagnose shows
player_stats=196,666 rows preserved from the legacy migration, but the recent in-game tank/round counts look smaller than expected given the activity.
- No user-visible game-side impact — plugin failures are swallowed.
- Log noise is significant —
errors_20260529.log was mostly this.
Repro
Any active campaign session on a server running bizzymod_stats.smx against the post-migration MySQL schema. Reproduces on every tank spawn after a map transition.
Where the evidence came from
GH Actions diagnose run on bogware/bizzymod-campaign:
https://github.com/bogware/bizzymod-campaign/actions/runs/26662028438
(Filed from the campaign-side investigation while debugging two unrelated server cvar issues — those are tracked in bogware/bizzymod-campaign#3.)
🤖 Filed via Claude Code
Surfaced by a fresh
diagnoserun on the Bizzymod Campaign server (2026-05-29). Both bugs are in the SourcePawn plugin, not the schema — the schema is doing its job (the FK constraint is what's catching bug B).Bug A — query buffer truncation in
bizzymod_stats.smxaddons/sourcemod/logs/errors_*.logis filling with MySQL errors where column names are being chopped mid-word:Diagnosis. The truncation pattern is the tell:
VALUE,VALU,VVALUESgetting clipped at various offsetsdamage_to_ta,damage_to_tandamage_to_tankcolumn inplayer_stats/tank_records''syntax errorThis is a buffer-size bug in query construction — a
Format()orStrCatcall where the destination is too small for the realistic column list. The same query is sometimes truncated at different lengths (VvsVALUvsVALUE), which suggests the size depends on a variable-length prefix (e.g. column-name list ordering, server identifier, map identifier).Quick triage paths in the plugin source:
INSERT INTO player_statsorINSERT INTO tank_records— these have the longest column counts (player_stats is ~50 columns after007_extended_stats.sql).char query[NNN]/char buf[NNN]declarations whereNNN < ~2048nearINSERTbuilders.VALUESis getting clipped means we're losing the closing half of the statement, so the buffer's running out partway through the column-list-then-VALUES build.Suggested fix direction:
DBI's transaction batching with one statement per query handle.Option 2 is the right long-term answer — also closes a latent SQLi/escaping surface area.
Bug B —
tank_records.fk_tr_mapforeign-key raceThroughout the play session:
Schema is from
migrations/mysql/013_round_shaping_events.sql:The plugin is inserting tank rows with a
map_idthat doesn't exist inmapsat insert time. Two plausible causes:c2m1_highway, the plugin records the spawn before (or concurrently with) the maps-table upsert for that map name. Async queue ordering.map_idonce per server lifetime; if maps is wiped or a migration renumbers IDs, the cache outlives the rows.The errors are tightly clustered around map-transition timestamps in the log (03:41-03:50 was the last cluster, all on map
c10m1_caves→c2m1_highway), which points strongly at hypothesis 1.Suggested fix direction:
INSERT IGNORE INTO maps (name) VALUES (?)thenSELECT id FROM maps WHERE name=?(or useINSERT ... ON DUPLICATE KEY UPDATE id=LAST_INSERT_ID(id)pattern) before chaining into tank_records insert.Impact
player_stats=196,666rows preserved from the legacy migration, but the recent in-game tank/round counts look smaller than expected given the activity.errors_20260529.logwas mostly this.Repro
Any active campaign session on a server running
bizzymod_stats.smxagainst the post-migration MySQL schema. Reproduces on every tank spawn after a map transition.Where the evidence came from
GH Actions diagnose run on
bogware/bizzymod-campaign:https://github.com/bogware/bizzymod-campaign/actions/runs/26662028438
(Filed from the campaign-side investigation while debugging two unrelated server cvar issues — those are tracked in bogware/bizzymod-campaign#3.)
🤖 Filed via Claude Code