ory/kratos · error
configuration value is not a valid URL
Error message
configuration value is not a valid URL: %s
What it means
Config.ParseURI delegates to ParseAbsoluteOrRelativeURI and then additionally requires the parsed URL to have a non-empty scheme. If the value parses as a URI but has no scheme (e.g. 'localhost:5432/db' or a bare path), this error is thrown because the library needs an absolute URL with an explicit scheme to know which driver/protocol to use.
Solutions
- Add the correct scheme to the configured URL, e.g. 'postgres://host/db', 'mysql://host/db', or 'sqlite://file?mode=rwc'.
- Check env var or YAML for a lost 'scheme://' prefix after templating or secret substitution.
- Consult the driver docs for the exact accepted DSN format and copy a known-good example.
- Pre-validate the value with url.Parse and check u.Scheme != "" before setting it in config.
Example fix
// before DSN=postgres:// DSN=localhost:5432/kratos // after DSN=postgres://localhost:5432/kratos?sslmode=disable
Defensive patterns
Strategy: validation
Validate before calling
u, err := url.ParseRequestURI(cfg.DSN)
if err != nil {
return err
}
if u.Scheme == "" {
return fmt.Errorf("DSN %q must include a scheme (e.g. postgres://)", cfg.DSN)
} Prevention
- Always write DSNs with an explicit scheme prefix (postgres://, mysql://, sqlite://).
- Add a config linter/schema check asserting the dsn field parses with a non-empty scheme.
- When templating DSNs, guard the scheme portion separately from the host part.
When it happens
Trigger: Calling (p *Config).ParseURI(rawUrl) where rawUrl passes URI parsing but parsed.Scheme == "" — e.g. dsn set to 'localhost:5432/db?sslmode=disable' or 'mydb.sqlite' without a 'sqlite://' prefix, or a DSN that lost its 'postgres://' prefix.
Common situations: Migrating from a driver that accepted bare host strings, config templates where the scheme portion was accidentally deleted, or users writing 'sqlite:./db.sqlite' variants the parser treats as scheme-less.
Understand the failure class
Background: "Invalid URL" errors: why new URL(), URI.parse, and reqwest::Url reject your string — missing scheme, whitespace, and bad path format — this error's family across 39 libraries.
Related errors
- configuration value not a valid URL
- you must provide `secrets.pagination` for FIPS compliance
- expected to get the DSN as an argument, or the…
- required config value "dsn" was not set
- you must provide `secrets.cipher` for FIPS compliance
AI-assisted analysis of ory/kratos@b86338da04 (2026-09-07).
Data as JSON: /api/errors/b1272c826c48ee72.
Report an issue: GitHub.
Appendix: source
Thrown at driver/config/config.go:1333
parsed, err := url.ParseRequestURI(u)
if err != nil {
return nil, errors.Wrapf(err, "configuration value not a valid URL: %s", rawUrl)
}
if frag != "" {
parsed.Fragment = frag
}
return parsed, nil
}
func (p *Config) ParseURI(rawUrl string) (*url.URL, error) {
parsed, err := p.ParseAbsoluteOrRelativeURI(rawUrl)
if err != nil {
return nil, err
}
if parsed.Scheme == "" {
return nil, errors.Errorf("configuration value is not a valid URL: %s", rawUrl)
}
return parsed, nil
}
func (p *Config) Tracing(ctx context.Context) *otelx.Config {
return p.GetProvider(ctx).TracingConfig("Ory Kratos")
}
func (p *Config) IsInsecureDevMode(ctx context.Context) bool {
return p.GetProvider(ctx).Bool("dev")
}
func (p *Config) IsBackgroundCourierEnabled(ctx context.Context) bool {
return p.GetProvider(ctx).Bool("watch-courier")
}
func (p *Config) CourierExposeMetricsPort(ctx context.Context) int {
return p.GetProvider(ctx).Int("expose-metrics-port")View on GitHub (pinned to b86338da04)