{"record":{"id":"0fced857ea6eabe5","repo":"ory/hydra","slug":"invalid-dsn-missing-scheme-separator","errorCode":null,"errorMessage":"invalid DSN: missing scheme separator","messagePattern":"invalid DSN: missing scheme separator","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"oryx/sqlxx/sqlxx.go","lineNumber":87,"sourceCode":"\treturn strings.Join(statements, \", \")\n}\n\nfunc OnConflictDoNothing(dialect string, columnNoop string) string {\n\tif dialect == \"mysql\" {\n\t\treturn fmt.Sprintf(\" ON DUPLICATE KEY UPDATE `%s` = `%s` \", columnNoop, columnNoop)\n\t} else {\n\t\treturn ` ON CONFLICT DO NOTHING `\n\t}\n}\n\n// ExtractSchemeFromDSN returns the scheme (e.g. `mysql`, `postgres`, etc) component in a DSN string,\n// as well as the remaining part of the DSN after the scheme separator.\n// It is an error to not have a scheme present.\n// This makes sense in the context of a DSN to be able to identify which database is in use.\nfunc ExtractSchemeFromDSN(dsn string) (string, string, error) {\n\tscheme, afterSchemeSeparator, schemeSeparatorFound := strings.Cut(dsn, \"://\")\n\tif !schemeSeparatorFound {\n\t\treturn \"\", \"\", errors.New(\"invalid DSN: missing scheme separator\")\n\t}\n\tif scheme == \"\" {\n\t\treturn \"\", \"\", errors.New(\"invalid DSN: empty scheme\")\n\t}\n\n\treturn scheme, afterSchemeSeparator, nil\n}\n\n// ExtractDbNameFromDSN returns the database name component in a DSN string.\nfunc ExtractDbNameFromDSN(dsn string) (string, error) {\n\t_, afterScheme, err := ExtractSchemeFromDSN(dsn)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\t_, afterSlash, slashFound := strings.Cut(afterScheme, \"/\")\n\tif !slashFound {\n\t\treturn \"\", nil","sourceCodeStart":69,"sourceCodeEnd":105,"githubUrl":"https://github.com/ory/hydra/blob/4174065ffb052799890f7480f5360a877a67ffc1/oryx/sqlxx/sqlxx.go#L69-L105","documentation":"ExtractSchemeFromDSN splits a DSN on the '://' separator and errors when no scheme is present. A DSN like 'postgres://...' yields scheme 'postgres'; a bare 'mydbhost/db' fails because the database type cannot be identified. This guard keeps downstream parsing from misinterpreting the DSN.","triggerScenarios":"Calling ExtractSchemeFromDSN (directly or via SQLiteDirFromDSN, ExtractDbNameFromDSN, ReplaceSchemeInDSN, DSNRedacted) with a DSN string that lacks '://', e.g. 'sqlite:///path/db' is fine but 'localhost:5432/db' or a bare file path is not.","commonSituations":"Env vars (DSN/DB_URL) set to a hostname or file path without a scheme; config values migrated from tools that accept scheme-less connection strings; shell quoting stripping part of the URL; accidentally passing a database name instead of a full DSN.","solutions":["Prefix the DSN with its proper scheme, e.g. 'postgres://' or 'sqlite://'","Check the environment variable / config key supplying the DSN for a missing scheme","Validate DSNs at startup with ExtractSchemeFromDSN and fail fast with a clear message","If a file path is intended for SQLite, use the 'sqlite://' scheme followed by the path"],"exampleFix":"// before\ndsn := \"localhost:5432/mydb\"\n// after\ndsn := \"postgres://localhost:5432/mydb\"","handlingStrategy":"validation","validationCode":"if !strings.Contains(dsn, \"://\") {\n\treturn fmt.Errorf(\"DSN %q must include a scheme, e.g. postgres://...\", redact(dsn))\n}","typeGuard":"func hasDSNScheme(dsn string) bool {\n\tscheme, _, ok := strings.Cut(dsn, \"://\")\n\treturn ok && scheme != \"\"\n}","tryCatchPattern":"scheme, rest, err := sqlxx.ExtractSchemeFromDSN(dsn)\nif err != nil {\n\treturn nil, fmt.Errorf(\"bad DSN (missing scheme?): %w\", err)\n}","preventionTips":["Always store full scheme-prefixed DSNs in env/config (e.g. postgres://, sqlite://)","Validate DSNs at startup, not at first query time","Don't pass bare hostnames or file paths where a DSN is expected"],"tags":["go","dsn","database","validation"],"backgroundTag":"invalid-dsn","analyzedSha":"4174065ffb052799890f7480f5360a877a67ffc1","analyzedAt":"2026-09-03T14:52:41.581Z","contentChangedAt":"2026-09-03T14:52:41.581Z","schemaVersion":2},"datasetVersion":"2026-09-10T17:17:09.494Z"}