{"record":{"id":"d1b1eb26015af3ad","repo":"knadh/listmonk","slug":"error-fetching-attachment-d-on-campaign-s-v","errorCode":null,"errorMessage":"error fetching attachment %d on campaign %s: %v","messagePattern":"error fetching attachment (.+?) on campaign (.+?): (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/manager/manager.go","lineNumber":701,"sourceCode":"\n\treturn funcs\n}\n\n// attachMedia loads any media/attachments from the media store and attaches\n// the byte blobs to the campaign. Inline attachments are skipped as they're\n// loaded earlier by LoadInlineImages().\nfunc (m *Manager) attachMedia(c *models.Campaign) error {\n\t// Already loaded if any non-inline attachment is present.\n\tfor _, a := range c.Attachments {\n\t\tif !a.IsInline {\n\t\t\treturn nil\n\t\t}\n\t}\n\n\tfor _, mid := range []int64(c.MediaIDs) {\n\t\ta, err := m.store.GetAttachment(int(mid))\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"error fetching attachment %d on campaign %s: %v\", mid, c.Name, err)\n\t\t}\n\t\tc.Attachments = append(c.Attachments, a)\n\t}\n\n\treturn nil\n}\n\n// LoadInlineImages resolves any <img ... data-embed ...> tags in the campaign\n// body and template body one time before CompileTemplate.\nfunc (m *Manager) LoadInlineImages(c *models.Campaign) error {\n\tif c.ContentType == models.CampaignContentTypePlain {\n\t\treturn nil\n\t}\n\n\tcidCache := make(map[string]string)\n\tbody, atts := m.applyInlineImages(c.Body, cidCache)\n\tc.Body = body\n","sourceCodeStart":683,"sourceCodeEnd":719,"githubUrl":"https://github.com/knadh/listmonk/blob/670c01717d48647093335cc23a6be6f4b79c3b6b/internal/manager/manager.go#L683-L719","documentation":"While preparing a campaign for sending, each media/attachment ID in the campaign's MediaIDs is fetched from the store via GetAttachment and appended to the campaign. If any attachment cannot be retrieved (missing row, DB error), this wrapped error identifying the attachment ID and campaign name is returned and campaign preparation aborts.","triggerScenarios":"A campaign references a media/attachment ID that was deleted from the media store, an ID belonging to a different installation, or the database lookup fails (connection error, corruption) during LoadAttachments.","commonSituations":"Admins deleting uploaded media that is still attached to a campaign, DB migrations losing media rows, restoring a campaign from backup without its media, or hardcoding attachment IDs in seed data.","solutions":["Re-attach valid media to the campaign or remove the dangling IDs from the campaign's MediaIDs","Restore the missing attachment row/file (re-upload the file via admin UI)","Check DB health if GetAttachment fails for existing IDs (connection, permissions)","Prune stale references: scan campaigns for MediaIDs with no matching media rows and clean them up"],"exampleFix":"// before\nMediaIDs: [3, 99] // 99 was deleted\n// after\nMediaIDs: [3] // or re-upload the file and use its new ID","handlingStrategy":"try-catch","validationCode":"// Audit campaign media references before sending\nfor _, id := range c.MediaIDs {\n    if !mediaExists(int(id)) {\n        return fmt.Errorf(\"campaign %q references missing media %d\", c.Name, id)\n    }\n}","typeGuard":null,"tryCatchPattern":"if err := m.LoadAttachments(c); err != nil {\n    if strings.Contains(err.Error(), \"error fetching attachment\") {\n        log.Printf(\"dangling media on campaign %s, pruning: %v\", c.Name, err)\n        return pruneMissingMedia(c) // drop bad IDs and retry\n    }\n    return err\n}","preventionTips":["Prevent deletion of media that campaigns reference (soft-delete with reference check)","Re-upload/restore media after DB restores before restarting campaigns","Periodically scan campaigns for MediaIDs missing from the media table","Avoid hardcoding attachment IDs in seed/fixture data"],"tags":["attachment","media","not-found","campaign"],"backgroundTag":"referenced-record-missing","analyzedSha":"670c01717d48647093335cc23a6be6f4b79c3b6b","analyzedAt":"2026-09-01T03:39:35.452Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}