Billionmail/BillionMail · error

failed to read HTML template: %w

Error message

failed to read HTML template: %w

What it means

readTemplateFiles loads the default subscribe-page HTML template from core/template/<baseFilename>.html; if os.ReadFile fails (missing file, wrong working directory, permissions) the error is wrapped with %w so the underlying os error (e.g. 'no such file or directory') is preserved.

Source

Thrown at core/internal/controller/subscribe_list/subscribe_list.go:109

// Update contact status: confirm (0,1), subscribe = 1
func updateContactStatus(email string, groupId int, status int) error {
	_, err := g.DB().Model("bm_contacts").
		Data(g.Map{"status": status, "active": 1}).
		Where("email", email).
		Where("group_id", groupId).
		Update()

	return err
}

func readTemplateFiles(baseFilename string) (htmlContent string, txtContent string, err error) {
	htmlPath := filepath.Join(public.AbsPath("../core/template"), baseFilename+".html")
	txtPath := filepath.Join(public.AbsPath("../core/template"), baseFilename+".txt")

	// Read HTML file
	htmlBytes, err := os.ReadFile(htmlPath)
	if err != nil {
		return "", "", fmt.Errorf("failed to read HTML template: %w", err)
	}

	// Read TXT file (allowed to not exist)
	txtBytes, err := os.ReadFile(txtPath)
	if err != nil {
		if !os.IsNotExist(err) {
			g.Log().Debugf(context.Background(),
				"failed to read TXT template: %v, path: %s", err, txtPath)
		}
		txtBytes = []byte("") // Return empty content if file does not exist
	}

	return string(htmlBytes), string(txtBytes), nil
}

func GetDefaultTemplate(emailType int) (html string, txt string) {
	var (
		defaultHtml string

View on GitHub (pinned to fc36c76c05)

Solutions

  1. Verify core/template/<baseFilename>.html exists on the deployed host and is readable by the process user.
  2. Redeploy including the template/ directory (or fix the Docker image/COPY step).
  3. Log/inspect public.AbsPath("../core/template") output and fix path resolution if the binary runs from an unexpected cwd.
  4. Restore correct file permissions if the file exists but is unreadable.

Example fix

// before
htmlPath := filepath.Join(public.AbsPath("../core/template"), baseFilename+".html")
// after
base := public.AbsPath("../core/template")
if _, err := os.Stat(base); err != nil {
    return "", "", fmt.Errorf("template dir missing at %s", base)
}
htmlPath := filepath.Join(base, baseFilename+".html")
Defensive patterns

Strategy: fallback

Validate before calling

const htmlPath = path.join(templateDir, baseFilename + '.html')
if (!fs.existsSync(htmlPath)) {
  throw new Error(`Deployed package missing template: ${htmlPath}`)
}

Try / catch

html, txt, err := readTemplateFiles(baseFilename)
if err != nil {
  log.Printf("template load failed: %v", err) // wrapped os error shows real path/problem
  return defaultEmbeddedTemplate // fallback to embedded template asset
}

Prevention

When it happens

Trigger: GetDefaultTemplate called while the HTML template file is absent from core/template, the template directory was not deployed (Docker image/volume missing), or AbsPath resolves to the wrong location because the binary's working directory changed.

Common situations: Deploying only the binary without the template/ directory; custom baseFilename that doesn't exist; container volume mounted over core/template; running tests from a directory where '../core/template' doesn't resolve.

Related errors


AI-assisted analysis of Billionmail/BillionMail@fc36c76c05 (2026-09-05). Data as JSON: /api/errors/46672e22130f898f. Report an issue: GitHub.