usememos/memos · critical

Parse DSN error

Error message

Parse DSN error

What it means

The MySQL driver builds its DSN via mergeDSN(profile.DSN) and then runs go-sql-driver's ParseDSN on the result. If ParseDSN fails, the raw cause is discarded and replaced with a plain "Parse DSN error", so the message gives no detail. Invalid DSN syntax includes bad parameter pairs, invalid net/addr, or unparseable user:pass segments.

Source

Thrown at store/db/mysql/mysql.go:32

type DB struct {
	db      *sql.DB
	profile *profile.Profile
	config  *mysql.Config
}

func NewDB(profile *profile.Profile) (store.Driver, error) {
	// Open MySQL connection with parameter.
	// multiStatements=true is required for migration.
	// See more in: https://github.com/go-sql-driver/mysql#multistatements
	dsn, err := mergeDSN(profile.DSN)
	if err != nil {
		return nil, err
	}

	driver := DB{profile: profile}
	driver.config, err = mysql.ParseDSN(dsn)
	if err != nil {
		return nil, errors.New("Parse DSN error")
	}

	driver.db, err = sql.Open("mysql", dsn)
	if err != nil {
		return nil, errors.Wrapf(err, "failed to open db: %s", profile.DSN)
	}

	return &driver, nil
}

func (d *DB) GetDB() *sql.DB {
	return d.db
}

func (d *DB) Close() error {
	return d.db.Close()
}

View on GitHub (pinned to 14d757ce1f)

Solutions

  1. Use the canonical format: user:password@tcp(127.0.0.1:3306)/memos?multiStatements=true
  2. Percent-encode special characters in the password (e.g. @ -> %40, / -> %2F)
  3. Reproduce locally with mysql.ParseDSN(dsn) to see the underlying error text
  4. Verify no conflicting duplicate params are introduced by mergeDSN

Example fix

# before
MEMOS_DSN="root:p@ss/word@tcp(localhost:3306)/memos"
# after
MEMOS_DSN="root:p%40ss%2Fword@tcp(localhost:3306)/memos"
Defensive patterns

Strategy: validation

Validate before calling

if _, err := mysql.ParseDSN(dsn); err != nil {
    log.Fatalf("invalid MySQL DSN: %v", err) // shows the real cause
}

Type guard

func validMySQLDSN(dsn string) bool {
    _, err := mysql.ParseDSN(dsn)
    return err == nil
}

Prevention

When it happens

Trigger: DSN strings like "user:pass@tcp(host:3306)/db?badParam" (parameter without '='), a missing '/dbname', or an addr without 'tcp(...)' wrapping; also mergeDSN appending conflicting params.

Common situations: Copy-pasting a PostgreSQL-style URL (postgres://...) into the MySQL DSN; special characters in passwords not percent-encoded; trailing spaces or shell-quoting mistakes in the MEMOS_DSN env var.

Related errors


AI-assisted analysis of usememos/memos@14d757ce1f (2026-08-15). Data as JSON: /api/errors/f6fccc242ea4adac. Report an issue: GitHub.