diff --git a/internal/agent/recovery_logging_test.go b/internal/agent/recovery_logging_test.go index 5534d07..8b60f43 100644 --- a/internal/agent/recovery_logging_test.go +++ b/internal/agent/recovery_logging_test.go @@ -86,6 +86,34 @@ func TestRecoveryLogsGrantCauseIdentityAndPersistedBackoff(t *testing.T) { } } +func TestResultJournalLogsOriginalDecodeCause(t *testing.T) { + out := captureRecoveryLogs(t) + j := ResultJournal{Root: privateRecoveryRoot(t)} + entry := ResultEntry{SourceEventID: "corrupt-result-event", DispatcherID: "11111111-1111-4111-8111-111111111111", TenantID: 1, Payload: []byte(`{"recording":{}}`)} + if err := j.SaveReady(entry); err != nil { + t.Fatal(err) + } + path, err := j.path(entry.SourceEventID) + if err != nil { + t.Fatal(err) + } + if err = os.WriteFile(path, []byte("{"), 0o600); err != nil { + t.Fatal(err) + } + err = j.Recover(context.Background(), func(context.Context, ResultEntry) error { return nil }) + if err == nil || !strings.Contains(err.Error(), "unexpected end of JSON input") { + t.Fatalf("original decode reason was hidden: %v", err) + } + for _, part := range []string{"decode_original_result", "unexpected end of JSON input", "event_digest"} { + if !strings.Contains(out.String(), part) { + t.Fatalf("missing %s: %s", part, out.String()) + } + } + if _, err = os.Stat(path); err != nil { + t.Fatal("corrupt original file must remain", err) + } +} + func TestResultJournalLogsIndividualCauseAndOriginalIdentity(t *testing.T) { out := captureRecoveryLogs(t) j := ResultJournal{Root: privateRecoveryRoot(t)} diff --git a/internal/agent/result_journal.go b/internal/agent/result_journal.go index 4beb00f..8dcd313 100644 --- a/internal/agent/result_journal.go +++ b/internal/agent/result_journal.go @@ -223,8 +223,12 @@ func (j ResultJournal) RecoverWithEnd(ctx context.Context, confirmEnd, report fu continue } info, err := os.Lstat(path) - if err != nil || !info.Mode().IsRegular() || info.Mode().Perm()&0077 != 0 || info.Size() > 1<<20 { - block("validate_result_file", errors.New("private result file is invalid")) + if err != nil { + block("stat_result_file", err) + continue + } + if !info.Mode().IsRegular() || info.Mode().Perm()&0077 != 0 || info.Size() > 1<<20 { + block("validate_result_file", fmt.Errorf("private result file is invalid: mode=%s size=%d", info.Mode(), info.Size())) continue } data, err := os.ReadFile(path) @@ -236,7 +240,7 @@ func (j ResultJournal) RecoverWithEnd(ctx context.Context, confirmEnd, report fu SourceEventID string `json:"source_event_id"` } if err := json.Unmarshal(data, &id); err != nil { - block("decode_original_result", errors.New("invalid private result journal")) + block("decode_original_result", fmt.Errorf("invalid private result journal: %w", err)) continue } entry, originalPath, err := j.load(id.SourceEventID)