Redact recipe-candidate failures in logs
A recipe-candidates request whose stored-recipe lookup failed logged the error unchanged, and that error quotes the search result's URL, which can carry a personal token, along with the database error text. dinners.Service.Candidates now wraps every failure other than a missing dinner in ErrCandidatesFailed, and writeDinnerError logs it as a fixed reason (candidates_failed) with the dinner id only, answering 500 internal. Not-found, not-configured and search-failed responses are unchanged. Fixes MQ-98-06 (Maestro 3759a975-f937-4284-81a2-9e61345a3aa3). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -85,6 +85,11 @@ var ErrGroundTimeout = errors.New("dinners: grounding the recipe took too long")
|
||||
// logged.
|
||||
var ErrGroundFailed = errors.New("dinners: grounding the recipe failed")
|
||||
|
||||
// ErrCandidatesFailed wraps any unexpected failure finding a dinner's
|
||||
// candidates. Its text may quote a candidate's URL and dependency errors,
|
||||
// so it is never logged.
|
||||
var ErrCandidatesFailed = errors.New("dinners: finding recipe candidates failed")
|
||||
|
||||
// ErrArchived is grounding an archived dinner by hand.
|
||||
var ErrArchived = errors.New("dinner archived")
|
||||
|
||||
@@ -332,19 +337,24 @@ type offerSearch struct {
|
||||
// replace the dinner's earlier candidates: one not offered again no longer
|
||||
// grounds it. They ground it for offerLifetime, and not at all once it is
|
||||
// grounded by hand, renamed or archived, even while this search runs. A
|
||||
// search that fails wraps recipes.ErrSearchFailed.
|
||||
// missing dinner is ErrNotFound; any other failure wraps
|
||||
// ErrCandidatesFailed, and a search that fails also wraps
|
||||
// recipes.ErrSearchFailed.
|
||||
func (s *Service) Candidates(ctx context.Context, id string) ([]Candidate, error) {
|
||||
// Tracked before the dinner is read: grounding, renaming or archiving
|
||||
// it after the read keeps this search from storing what it found.
|
||||
keep, done := s.trackOffers(id)
|
||||
defer done()
|
||||
row, err := s.get(ctx, id)
|
||||
if err != nil {
|
||||
if errors.Is(err, ErrNotFound) {
|
||||
return nil, err
|
||||
}
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%w: dinner %s: %w", ErrCandidatesFailed, id, err)
|
||||
}
|
||||
offers, err := s.grounder.Candidates(ctx, row.Name, row.Name, MaxCandidates)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("%w: dinner %s: %w", ErrCandidatesFailed, id, err)
|
||||
}
|
||||
out := make([]Candidate, len(offers))
|
||||
urls := make(map[string]string, len(offers))
|
||||
|
||||
Reference in New Issue
Block a user