Billionmail/BillionMail · error
failed to update alias: %v
Error message
failed to update alias: %v
What it means
setCatchall wraps a DB UPDATE on the `alias` table (setting the catchall `goto` target for the catchall address of a domain) when the row fails to update. GoFrame's Model.Update() returns the underlying PostgreSQL driver error, and this wrapper adds context that the alias update step of domain Add/Update failed. It means the catchall forwarding record could not be persisted.
Source
Thrown at core/internal/service/domains/domains.go:63
func setCatchall(ctx context.Context, domainName, catchall string) error {
address := fmt.Sprintf("@%s", domainName)
if catchall != "" {
var count int
count, err := g.DB().Model("alias").
Where("address", address).
Where("domain", domainName).
Count()
if err != nil {
return fmt.Errorf("failed to check alias existence: %v", err)
}
if count > 0 {
_, err = g.DB().Model("alias").
Where("address", address).
Where("domain", domainName).
Data(g.Map{"goto": catchall, "active": 1, "update_time": time.Now().Unix()}).
Update()
if err != nil {
return fmt.Errorf("failed to update alias: %v", err)
}
} else {
_, err = g.DB().Model("alias").Data(g.Map{
"address": address,
"goto": catchall,
"domain": domainName,
"active": 1,
"create_time": time.Now().Unix(),
"update_time": time.Now().Unix(),
}).Insert()
if err != nil {
return fmt.Errorf("failed to insert alias: %v", err)
}
}
} else {
// catchall is empty, disabled
_, _ = g.DB().Model("alias").
Where("address", address).View on GitHub (pinned to fc36c76c05)
Solutions
- Verify PostgreSQL is reachable and the `alias` table exists (check the wrapped %v detail for connection vs SQL errors)
- Check the DB user has UPDATE privilege on the alias table
- Inspect for concurrent deletes/unique constraints on (address, domain) and re-run the domain update
- Recreate the catchall alias row manually if it was deleted mid-operation, then retry
Example fix
// before
_, err = g.DB().Model("alias").Where("address", address).Where("domain", domainName).Data(...).Update()
// after
result, err := g.DB().Model("alias").Where("address", address).Where("domain", domainName).Data(g.Map{"goto": catchall, "active": 1}).Update()
if err != nil { return fmt.Errorf("failed to update alias: %v", err) }
if n, _ := result.RowsAffected(); n == 0 { return fmt.Errorf("catchall alias %s not found for domain %s", address, domainName) } Defensive patterns
Strategy: try-catch
Validate before calling
var n int
countErr := g.DB().Model("alias").Where("address", address).Where("domain", domainName).Count(&n)
if countErr != nil { return fmt.Errorf("alias table unavailable: %v", countErr) } Try / catch
result, err := g.DB().Model("alias").Where(...).Data(...).Update()
if err != nil {
if strings.Contains(err.Error(), "connection refused") { /* surface DB-down to admin UI */ }
return fmt.Errorf("failed to update alias: %v", err)
} Prevention
- Health-check PostgreSQL before domain mutations
- Keep migrations ensuring the alias table run at startup
- Avoid concurrent edits to the same domain (lock or optimistic versioning)
- Log the wrapped driver error verbatim for diagnosis
When it happens
Trigger: Calling domains.Add or domains.Update with a non-empty Catchall when the UPDATE ... WHERE address=<catchall address> AND domain=<domain> fails — e.g. DB connection down, alias table missing/locked, constraint violation, or the row was concurrently deleted between the earlier count query and the update.
Common situations: Postgres container stopped or unreachable; `alias` table dropped by a failed migration; database user lacking UPDATE privilege on alias; deadlocks from concurrent domain edits; duplicate catchall rows causing unexpected affected-row behavior.
Related errors
- failed to insert alias: %v
- failed to check alias existence: %v
- failed to get all domains: %w
- fail to check domain: %w
- Failed to add exception recipient: %w
AI-assisted analysis of Billionmail/BillionMail@fc36c76c05 (2026-09-05).
Data as JSON: /api/errors/b385b7b9f84fcf2e.
Report an issue: GitHub.