Billionmail/BillionMail · critical
failed to get option from database:
Error message
failed to get option from database:
What it means
When the value is not in cache, GetOption queries the bm_options table (WHERE name = key). Any database error returned by the ORM is wrapped as "failed to get option from database: <driver error>". This signals an infrastructure/DB problem or a schema issue, not a missing option (that's the separate 'option not found' error).
Source
Thrown at core/internal/service/public/options_mgr.go:86
// Try to get from cache
cacheKey := o.buildCacheKey(key)
cached, err := o.cache.Get(ctx, cacheKey)
var jsonValue string
if err != nil || cached == nil {
// Cache miss, read from database
var result struct {
Value string `json:"value"`
}
err := g.DB().Model("bm_options").
Where("name", key).
Fields("value").
Scan(&result)
if err != nil {
return errors.New("failed to get option from database: " + err.Error())
}
if result.Value == "" {
return errors.New("option not found")
}
jsonValue = result.Value
// Store in cache
if err := o.cache.Set(ctx, cacheKey, jsonValue, o.expiration); err != nil {
g.Log().Warning(ctx, "Failed to set option cache:", err)
}
} else {
// Cache hit
jsonValue = cached.String()
}
// DeserializeView on GitHub (pinned to fc36c76c05)
Solutions
- Read the wrapped driver message after the colon to identify the root cause (connection refused, relation does not exist, etc.).
- Verify PostgreSQL is running and reachable (docker compose ps, connection settings).
- Ensure schema migrations have created the bm_options table.
- Check Redis cache health — repeated DB fallbacks due to cache outages amplify DB load.
Defensive patterns
Strategy: retry
Validate before calling
// ping DB before batch option loads
if err := g.DB().PingContext(ctx); err != nil {
return fmt.Errorf("database unavailable: %w", err)
} Try / catch
if err := public.GetOption(ctx, key, &out); err != nil {
if strings.Contains(err.Error(), "failed to get option from database") {
// transient DB issue: retry with backoff
return retry.Do(3, 500*time.Millisecond, func() error {
return public.GetOption(ctx, key, &out)
})
}
return err
} Prevention
- Monitor PostgreSQL health and alert on connection failures.
- Run migrations before app startup so bm_options always exists.
- Keep Redis cache healthy to reduce DB fallbacks.
- Distinguish DB errors from 'option not found' by matching the message prefix.
When it happens
Trigger: Database is down/unreachable, bm_options table missing (schema not migrated), or connection pool exhausted when GetOption falls through to the DB after a cache miss.
Common situations: Fresh deployment before migrations run; Postgres restarted or container not up; wrong DB credentials in config causing connection failures.
Understand the failure class
Background: Database query failed: Internal Server Error 500s wrapping SQL, Prisma, and connection failures — what to check first — this error's family across 16 libraries.
Related errors
- Failed to get the total number of exception recipients: %w
- Failed to get the exception recipient list: %w
- failed to get account: %w
- enqueue video job: %w
- load contact attribs: %w
AI-assisted analysis of Billionmail/BillionMail@fc36c76c05 (2026-09-05).
Data as JSON: /api/errors/acb4405933dc0aee.
Report an issue: GitHub.