Skip to content

Auth pod crash-loops on invalid GOTRUE_SMTP_HEADERS JSON — fatal Validate() breaks the entire service #2533

@jamesburns

Description

@jamesburns

Summary

SMTPConfiguration.Validate() (introduced in v2.189.0) fatally errors at startup when GOTRUE_SMTP_HEADERS contains invalid JSON, which crash-loops the entire auth service. Disabling SMTP at the dashboard level does not help — the value is parsed regardless. Currently affecting our production Supabase project — all auth endpoints (/token, /authorize, /user) return Envoy 503 (ECONNREFUSED, x-envoy-attempt-count: 6), while /health and /settings still respond because they don't go through GoTrue.

Reproducing

Set GOTRUE_SMTP_HEADERS to anything that's invalid JSON (e.g. contains \$, which is what's currently being injected into our project by Supabase's platform layer):

GOTRUE_SMTP_HEADERS='{"X-SES-Message-Tags":["…=\$messageType"]}'

Auth fails to boot with:

{"level":"fatal","msg":"Failed to load configuration: conf: SMTP headers not a map[string][]string format: invalid character '$' in string escape code"}

Reference (v2.189.0):

This same code is unchanged in rc2.190.0-rc.6.

Why this is a problem

  1. SMTP custom headers are an optional feature. A malformed value here shouldn't take down sign-in, refresh-token, OAuth, MFA, and every other auth flow. Fail-closed on auth core, fail-open on cosmetic email metadata.
  2. The value is often platform-injected, not user-set. On Supabase-hosted projects, the customer can't see or write to this env var via the dashboard or the public Management API — there's no smtp_headers field in the documented PATCH /v1/projects/{ref}/config/auth schema. So when the platform-injection layer ships a bad value (as is happening for us right now), customers cannot self-recover.
  3. Disabling SMTP doesn't gate the parse. Validate() parses headers whenever c.Headers != "", regardless of whether Host/User/Pass are set or whether the SMTP toggle is on.

Proposed fix

In internal/conf/configuration.go, change SMTPConfiguration.Validate() to log a warning and skip the headers feature instead of returning a fatal error:

func (c *SMTPConfiguration) Validate() error {
    headers := make(map[string][]string)

    if c.Headers != "" {
        if err := json.Unmarshal([]byte(c.Headers), &headers); err != nil {
            // Don't crash the auth service over an optional feature.
            // Log and continue without custom headers.
            logrus.WithError(err).Warn("conf: SMTP headers not a valid map[string][]string; ignoring")
            headers = nil
        }
    }

    if len(headers) > 0 {
        c.normalizedHeaders = headers
    }

    mail := gomail.NewMessage()
    c.fromAddress = mail.FormatAddress(c.AdminEmail, c.SenderName)

    return nil
}

Optionally also gate on c.Host != "" so the headers aren't parsed when SMTP is disabled entirely.

Impact for us, right now

100% of authentication is broken for our production project. I've filed a support ticket asking Supabase to clear/correct the platform-injected value, but I'm filing this here because:

  • The injected-value bug likely affects every project upgraded to v2.189.0 with the SES/Postmark headers default that has the same malformed value.
  • The current Validate() design makes a recoverable misconfiguration into a total auth outage — that's worth fixing regardless of whether the platform issue is fully resolved.

Environment

  • GoTrue version: v2.189.0 (also reproduces against rc2.190.0-rc.6)
  • Supabase-hosted (West US — North California region)
  • Started: ~2026-05-15 ~16:00 UTC

Happy to send a PR with the Validate() fix if it would help. Thanks.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions