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

  1. Verify PostgreSQL is reachable and the `alias` table exists (check the wrapped %v detail for connection vs SQL errors)
  2. Check the DB user has UPDATE privilege on the alias table
  3. Inspect for concurrent deletes/unique constraints on (address, domain) and re-run the domain update
  4. 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

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


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