{"record":{"id":"d458ba0a047e351c","repo":"Billionmail/BillionMail","slug":"query-password-failed-w","errorCode":null,"errorMessage":"query password failed: %w","messagePattern":"query password failed: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/internal/service/mail_service/sending.go","lineNumber":157,"sourceCode":"\tes.Password, err = PasswordPlainByEmail(context.Background(), email)\n\n\tif err != nil {\n\t\treturn\n\t}\n\n\tif !es.IsConfigured() {\n\t\terr = errors.New(\"Email Sender not configured\")\n\t\treturn\n\t}\n\n\treturn\n}\n\n// PasswordPlainByEmail\nfunc PasswordPlainByEmail(ctx context.Context, email string) (string, error) {\n\tval, err := g.DB().Model(\"mailbox\").Where(\"username\", email).Value(\"password_encode\")\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"query password failed: %w\", err)\n\t}\n\tif val.IsEmpty() {\n\t\treturn \"\", fmt.Errorf(\"password not found for %s\", email)\n\t}\n\trawHex, err := hex.DecodeString(val.String())\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"hex decode failed: %w\", err)\n\t}\n\tplainBytes, err := base64.StdEncoding.DecodeString(string(rawHex))\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"base64 decode failed: %w\", err)\n\t}\n\treturn string(plainBytes), nil\n}\n\n// Close closes the SMTP connection\nfunc (e *EmailSender) Close() {\n\t_ = e.Disconnect()","sourceCodeStart":139,"sourceCodeEnd":175,"githubUrl":"https://github.com/Billionmail/BillionMail/blob/fc36c76c050c3775c5e899faf7403cf0262d2744/core/internal/service/mail_service/sending.go#L139-L175","documentation":"PasswordPlainByEmail reads the mailbox row's password_encode column for the given username. A database error during that query (connection failure, table missing, SQL error) is wrapped as 'query password failed', keeping the original error via %w for errors.Is/As inspection.","triggerScenarios":"NewEmailSenderWithLocal → PasswordPlainByEmail when the mailbox table is unavailable, the DB connection is broken, or the query is rejected (permissions, schema mismatch).","commonSituations":"Postgres down or restarting; migrations not applied so `mailbox` table is absent; DB user lacks SELECT on mailbox; connection pool exhausted.","solutions":["Check DB connectivity and server logs; confirm Postgres is up.","Run migrations to ensure the mailbox table exists with the password_encode column.","Verify the app's DB credentials have SELECT permission on mailbox.","Use errors.Is/errors.As on the wrapped cause to identify the driver-level error."],"exampleFix":"// before\npass, err := PasswordPlainByEmail(ctx, email)\nif err != nil { return err }\n// after\npass, err := PasswordPlainByEmail(ctx, email)\nif err != nil {\n    var dbErr *pgconn.PgError\n    if errors.As(err, &dbErr) {\n        log.Printf(\"database error fetching mailbox password: %s\", dbErr.Message)\n    }\n    return err\n}","handlingStrategy":"retry","validationCode":"hasTable, err := g.DB().Model(\"information_schema.tables\").\n    Where(\"table_name = 'mailbox'\").Count()\nif err != nil || hasTable == 0 {\n    return errors.New(\"mailbox table missing — run migrations before sending\")\n}","typeGuard":null,"tryCatchPattern":"pass, err := PasswordPlainByEmail(ctx, email)\nif err != nil {\n    var cause error\n    errors.As(err, &cause) // wrapped with %w, inspect DB driver error\n    if isTransient(cause) {\n        time.Sleep(time.Second)\n        pass, err = PasswordPlainByEmail(ctx, email)\n    }\n    return pass, err\n}","preventionTips":["Run migrations on every deploy so mailbox schema exists.","Monitor DB connectivity with a readiness probe.","Grant the app DB role SELECT on mailbox."],"tags":["database","smtp","password","mailbox"],"backgroundTag":"database-query-failed","analyzedSha":"fc36c76c050c3775c5e899faf7403cf0262d2744","analyzedAt":"2026-09-05T21:28:54.019Z","contentChangedAt":"2026-09-05T21:28:54.019Z","schemaVersion":2},"datasetVersion":"2026-09-12T22:17:10.623Z"}