From 1d4f5dbce2e928ceebe071e5c8a60c2cb350c15c Mon Sep 17 00:00:00 2001 From: Quick104 <31828688+Quick104@users.noreply.github.com> Date: Tue, 21 Jul 2026 08:17:03 -0400 Subject: [PATCH] fix(diagnostics): bind empty playback_session_ids and log ingest failures pgx binds a nil Go slice as SQL NULL, which bypasses the column's '{}' default and violates its NOT NULL constraint, so reports without playback session ids failed to insert. Bind an empty slice instead. The ingest path also swallowed the underlying insert and bundle validation errors, logging only a generic rejection reason; both sites now log the real error so failures are diagnosable from server logs. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_012e3QjbPo96ed9Mn2qRiUkh --- internal/diagnostics/repo.go | 8 +++++++- internal/diagnostics/service.go | 12 ++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/internal/diagnostics/repo.go b/internal/diagnostics/repo.go index 9797aafa..1f2b2aa0 100644 --- a/internal/diagnostics/repo.go +++ b/internal/diagnostics/repo.go @@ -69,6 +69,12 @@ func (r *PostgresRepository) InsertReceiving(ctx context.Context, input InsertRe } crashSummary := truncateCrashSummary(input.CrashSummary) + sessionIDs := input.PlaybackSessionIDs + if sessionIDs == nil { + // pgx binds a nil slice as SQL NULL, which bypasses the column's + // '{}' default and violates its NOT NULL constraint. + sessionIDs = []string{} + } for attempt := 0; attempt < retries; attempt++ { id := reportIDGenerator() if _, err := uuid.Parse(id); err != nil { @@ -95,7 +101,7 @@ func (r *PostgresRepository) InsertReceiving(ctx context.Context, input InsertRe `, id, shortID, input.UserID, nullableString(input.ProfileID), input.CapturedAt, strings.TrimSpace(input.ReportType), strings.TrimSpace(input.Platform), strings.TrimSpace(input.AppVersion), nullableString(crashSummary), - string(input.Manifest), input.PlaybackSessionIDs).Scan(&insertedID, &insertedShortID) + string(input.Manifest), sessionIDs).Scan(&insertedID, &insertedShortID) if errors.Is(err, pgx.ErrNoRows) { continue } diff --git a/internal/diagnostics/service.go b/internal/diagnostics/service.go index 2121d490..1f9ba254 100644 --- a/internal/diagnostics/service.go +++ b/internal/diagnostics/service.go @@ -172,6 +172,12 @@ func (s *Service) Ingest(ctx context.Context, userID int, profileID *string, man reason := "insert_failed" if errors.Is(err, ErrQuotaExceeded) { reason = "quota_exceeded" + } else { + s.logger.ErrorContext(ctx, "diagnostic report insert failed", + "component", "diagnostics", + "user_id", userID, + "error", err, + ) } s.logRejected(ctx, "", userID, manifest.Report.Platform, manifest.Report.Type, manifest.Archive.Bytes, reason) return IngestResult{}, err @@ -182,6 +188,12 @@ func (s *Service) Ingest(ctx context.Context, userID int, profileID *string, man if err != nil { s.compensateFailedUpload(ctx, reserved.ID, key) rejectErr := classifyBundleUploadError(err) + s.logger.ErrorContext(ctx, "diagnostic bundle validation failed", + "component", "diagnostics", + "report_id", reserved.ID, + "user_id", userID, + "error", err, + ) s.logRejected(ctx, reserved.ID, userID, manifest.Report.Platform, manifest.Report.Type, manifest.Archive.Bytes, rejectReason(rejectErr)) return IngestResult{}, rejectErr }