{"record":{"id":"f6fccc242ea4adac","repo":"usememos/memos","slug":"parse-dsn-error","errorCode":null,"errorMessage":"Parse DSN error","messagePattern":"Parse DSN error","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"store/db/mysql/mysql.go","lineNumber":32,"sourceCode":"type DB struct {\n\tdb      *sql.DB\n\tprofile *profile.Profile\n\tconfig  *mysql.Config\n}\n\nfunc NewDB(profile *profile.Profile) (store.Driver, error) {\n\t// Open MySQL connection with parameter.\n\t// multiStatements=true is required for migration.\n\t// See more in: https://github.com/go-sql-driver/mysql#multistatements\n\tdsn, err := mergeDSN(profile.DSN)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tdriver := DB{profile: profile}\n\tdriver.config, err = mysql.ParseDSN(dsn)\n\tif err != nil {\n\t\treturn nil, errors.New(\"Parse DSN error\")\n\t}\n\n\tdriver.db, err = sql.Open(\"mysql\", dsn)\n\tif err != nil {\n\t\treturn nil, errors.Wrapf(err, \"failed to open db: %s\", profile.DSN)\n\t}\n\n\treturn &driver, nil\n}\n\nfunc (d *DB) GetDB() *sql.DB {\n\treturn d.db\n}\n\nfunc (d *DB) Close() error {\n\treturn d.db.Close()\n}\n","sourceCodeStart":14,"sourceCodeEnd":50,"githubUrl":"https://github.com/usememos/memos/blob/14d757ce1fb31c78590f374bc042f8dbedbc20d7/store/db/mysql/mysql.go#L14-L50","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Use the canonical format: user:password@tcp(127.0.0.1:3306)/memos?multiStatements=true","Percent-encode special characters in the password (e.g. @ -> %40, / -> %2F)","Reproduce locally with mysql.ParseDSN(dsn) to see the underlying error text","Verify no conflicting duplicate params are introduced by mergeDSN"],"exampleFix":"# before\nMEMOS_DSN=\"root:p@ss/word@tcp(localhost:3306)/memos\"\n# after\nMEMOS_DSN=\"root:p%40ss%2Fword@tcp(localhost:3306)/memos\"","handlingStrategy":"validation","validationCode":"if _, err := mysql.ParseDSN(dsn); err != nil {\n    log.Fatalf(\"invalid MySQL DSN: %v\", err) // shows the real cause\n}","typeGuard":"func validMySQLDSN(dsn string) bool {\n    _, err := mysql.ParseDSN(dsn)\n    return err == nil\n}","tryCatchPattern":null,"preventionTips":["Percent-encode special characters in DSN passwords","Pre-validate DSNs with go-sql-driver's ParseDSN during config load to get detailed errors"],"tags":["database","mysql","dsn","startup"],"backgroundTag":null,"analyzedSha":"14d757ce1fb31c78590f374bc042f8dbedbc20d7","analyzedAt":"2026-08-15T09:27:36.538Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}