{"record":{"id":"487a23974615d218","repo":"iflytek/astron-agent","slug":"mysql-dsn-is-nil","errorCode":null,"errorMessage":"mysql dsn is nil","messagePattern":"mysql dsn is nil","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/tenant/tools/database/database.go","lineNumber":114,"sourceCode":"\tcredentials config.TenantBootstrapCredentials,\n) error {\n\tif err := runMigrations(client); err != nil {\n\t\treturn err\n\t}\n\tif err := reconcileTenantBootstrap(client, credentials); err != nil {\n\t\treturn err\n\t}\n\tlog.Printf(\"tenant bootstrap credentials reconciled\")\n\treturn nil\n}\n\nfunc (db *Database) GetMysql() *sql.DB {\n\treturn db.mysql\n}\n\nfunc ensureMySQLDatabase(parsedDsn *mysql.Config) error {\n\tif parsedDsn == nil {\n\t\treturn errors.New(\"mysql dsn is nil\")\n\t}\n\tif parsedDsn.DBName == \"\" {\n\t\treturn errors.New(\"mysql database name is empty\")\n\t}\n\n\tadminDsn := parsedDsn.Clone()\n\tdbName := adminDsn.DBName\n\tadminDsn.DBName = \"\"\n\n\tclient, err := sql.Open(\"mysql\", adminDsn.FormatDSN())\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer func() {\n\t\t_ = client.Close()\n\t}()\n\n\tif err := client.Ping(); err != nil {","sourceCodeStart":96,"sourceCodeEnd":132,"githubUrl":"https://github.com/iflytek/astron-agent/blob/5e758547a83371a5a4b29dadf4ac03e8dd527635/core/tenant/tools/database/database.go#L96-L132","documentation":"ensureMySQLDatabase receives the parsed go-sql-driver *mysql.Config and guards against a nil pointer before it clones the DSN to run admin (database-creating) statements. A nil parsedDsn means the DSN parse step upstream produced no config object at all, so the function cannot proceed.","triggerScenarios":"buildMysql calls ensureMySQLDatabase with the result of mysql.ParseDSN when that call returned nil (typically alongside a parse error that was mishandled or an empty DSN input).","commonSituations":"Programmer error where the ParseDSN error is ignored and nil is passed through; refactoring that changed the DSN construction and dropped the error check; calling ensureMySQLDatabase directly in tests with no config.","solutions":["In buildMysql, check the error from mysql.ParseDSN before using the returned config and return the parse error instead of continuing.","Ensure the DSN string built in parseMysqlConfig is non-empty and well-formed before parsing.","Add a defensive nil check/log where the *mysql.Config originates so the nil never propagates.","Write a unit test for buildMysql covering the empty-DSN path."],"exampleFix":"// before\nparsedDsn, _ := mysql.ParseDSN(dsn)\nif err := ensureMySQLDatabase(parsedDsn); err != nil {\n    return nil, err\n}\n\n// after\nparsedDsn, err := mysql.ParseDSN(dsn)\nif err != nil {\n    return nil, fmt.Errorf(\"parse mysql dsn: %w\", err)\n}\nif err := ensureMySQLDatabase(parsedDsn); err != nil {\n    return nil, err\n}","handlingStrategy":"type-guard","validationCode":"if parsedDsn == nil {\n\treturn errors.New(\"mysql dsn parse failed before ensureMySQLDatabase\")\n}","typeGuard":"func dsnConfigIsValid(c *mysql.Config) bool { return c != nil && c.DBName != \"\" }","tryCatchPattern":"if err != nil {\n\treturn fmt.Errorf(\"build mysql: %w\", err)\n}","preventionTips":["Never discard errors from mysql.ParseDSN with _.","Run go vet / errcheck so ignored errors are caught in CI.","Write table-driven tests for DSN construction covering empty inputs."],"tags":["mysql","null-pointer","dsn","go"],"backgroundTag":"null-argument","analyzedSha":"5e758547a83371a5a4b29dadf4ac03e8dd527635","analyzedAt":"2026-09-12T08:03:51.356Z","contentChangedAt":"2026-09-12T08:03:51.356Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}