knadh/listmonk · error

template %d not found

Error message

template %d not found

What it means

GetTpl looks up a compiled template by numeric ID from the manager's in-memory template cache under a read lock. If no template with that ID is cached, this error is returned. It means the template either never existed, was deleted, or hasn't been loaded into the manager's cache yet.

Source

Thrown at internal/manager/manager.go:363

	m.tpls[id] = tpl
	m.tplsMut.Unlock()
}

// DeleteTpl deletes a cached template.
func (m *Manager) DeleteTpl(id int) {
	m.tplsMut.Lock()
	delete(m.tpls, id)
	m.tplsMut.Unlock()
}

// GetTpl returns a cached template.
func (m *Manager) GetTpl(id int) (*models.Template, error) {
	m.tplsMut.RLock()
	tpl, ok := m.tpls[id]
	m.tplsMut.RUnlock()

	if !ok {
		return nil, fmt.Errorf("template %d not found", id)
	}

	return tpl, nil
}

// TemplateFuncs returns the template functions to be applied into
// compiled campaign templates.
func (m *Manager) TemplateFuncs(c *models.Campaign) template.FuncMap {
	f := template.FuncMap{
		"TrackLink": func(url string, msg *CampaignMessage) string {
			if m.cfg.DisableTracking {
				return url
			}

			subUUID := msg.Subscriber.UUID
			if !m.cfg.IndividualTracking {
				subUUID = dummyUUID
			}

View on GitHub (pinned to 670c01717d)

Solutions

  1. Verify the template ID exists (query the templates table / admin UI) and correct the reference
  2. Re-load or refresh the manager's template cache (re-init or reload) so deleted/renamed templates are reflected
  3. Repoint the campaign or code to an existing template ID
  4. Ensure initialization order: load templates before starting the campaign scanner that may call GetTpl

Example fix

// before
tpl, err := m.GetTpl(42) // stale id from deleted template
// after
id, err := resolveTemplateIDByName("welcome-email")
if err != nil { return err }
tpl, err := m.GetTpl(id)
Defensive patterns

Strategy: try-catch

Validate before calling

// Confirm the template exists before rendering a campaign
if !templateExistsInStore(templateID) {
    return fmt.Errorf("template %d does not exist; repoint campaign to a valid template", templateID)
}

Try / catch

tpl, err := mgr.GetTpl(id)
if err != nil {
    if strings.Contains(err.Error(), "not found") {
        tpl, err = loadFallbackTemplate(mgr)
    }
    if err != nil { return err }
}

Prevention

When it happens

Trigger: Requesting a campaign/template render with a template ID that was deleted (campaign still referencing it), an ID from another installation/database, or calling GetTpl before the manager finished loading templates from the store.

Common situations: Stale campaign rows pointing at removed templates, race at startup where a campaign scan runs before template cache warm-up completes, hardcoded template IDs in scripts/tests after DB resets, or importing data without templates.

Understand the failure class

Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.

Related errors


AI-assisted analysis of knadh/listmonk@670c01717d (2026-09-01). Data as JSON: /api/errors/80ed9291483c356f. Report an issue: GitHub.