{"record":{"id":"6a1c10a86f89447a","repo":"dapr/dapr","slug":"failed-to-join-path-for-authorization-endpoint-w","errorCode":null,"errorMessage":"failed to join path for authorization endpoint: %w","messagePattern":"failed to join path for authorization endpoint: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/sentry/server/oidc/oidc.go","lineNumber":154,"sourceCode":"\tauthorizeEndpoint := AuthorizationEndpoint\n\tif opts.PathPrefix != nil && *opts.PathPrefix != \"/\" {\n\t\tif before, ok := strings.CutSuffix(*opts.PathPrefix, \"/\"); ok {\n\t\t\topts.PathPrefix = new(before)\n\t\t}\n\n\t\tvar err error\n\n\t\tjwksEndpoint, err = url.JoinPath(*opts.PathPrefix, JWKSEndpoint)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"failed to join path for JWKS endpoint: %w\", err)\n\t\t}\n\t\toidcEndpoint, err = url.JoinPath(*opts.PathPrefix, OIDCDiscoveryEndpoint)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"failed to join path for OIDC discovery endpoint: %w\", err)\n\t\t}\n\t\tauthorizeEndpoint, err = url.JoinPath(*opts.PathPrefix, authorizeEndpoint)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"failed to join path for authorization endpoint: %w\", err)\n\t\t}\n\n\t\tlog.Infof(\"Using path prefix %q for OIDC HTTP endpoints\", *opts.PathPrefix)\n\t}\n\n\treturn &Server{\n\t\tport:              opts.Port,\n\t\tlistenAddress:     opts.ListenAddress,\n\t\tjwks:              opts.JWKS,\n\t\thtarget:           opts.Healthz.AddTarget(\"oidc-server\"),\n\t\tjwksURI:           opts.JWKSURI,\n\t\tallowedHosts:      opts.AllowedHosts,\n\t\ttlsCertPath:       opts.TLSCertPath,\n\t\ttlsKeyPath:        opts.TLSKeyPath,\n\t\tjwtIssuer:         opts.JWTIssuer,\n\t\tpathPrefix:        opts.PathPrefix,\n\t\tauthorizeEndpoint: authorizeEndpoint,\n\t\tjwksEndpoint:      jwksEndpoint,","sourceCodeStart":136,"sourceCodeEnd":172,"githubUrl":"https://github.com/dapr/dapr/blob/74ad41702745709bb15fe2114ff693b8c59bc3cc/pkg/sentry/server/oidc/oidc.go#L136-L172","documentation":"Dapr sentry's OIDC server constructor joins the configured PathPrefix with the authorization endpoint path '/authorize' using url.JoinPath. As with the sibling JWKS/discovery joins, the only failure mode is a base prefix that cannot be parsed as a URL reference — control characters, malformed percent-escapes, or a NUL byte. Valid path prefixes never produce this error.","triggerScenarios":"PathPrefix option containing an unparseable substring (e.g. '/dapr%2', '/da\\x00pr'), set while PathPrefix != nil and != '/'. Because the two preceding joins run first, this specific error surfaces only when those passed and this join's base is rejected.","commonSituations":"Issuer path built by string concatenation from multiple env vars where one carries a bad escape or trailing control character; configuration managed by Helm/Ansible templates inserting stray '%' or whitespace artifacts into the issuer URL.","solutions":["Dump the effective PathPrefix with %q (fmt.Sprintf(\"%q\", *opts.PathPrefix)) and strip the offending characters at the source (env var, chart value, CLI flag)","Validate the prefix once up front: reject anything matching [^A-Za-z0-9._~/-] before calling NewServer","Set the prefix from a parsed issuer URL's .Path field rather than a raw string","Add a regression test covering the exact prefix value your deployment pipeline produces"],"exampleFix":"// before\nprefix := fmt.Sprintf(\"%s/%s\", baseEnv, subEnv) // may embed '%zz' or '\\n'\nauthorize, err := url.JoinPath(prefix, \"/authorize\")\n\n// after\nu, err := url.Parse(issuerURL)\nif err != nil { return err }\nprefix := u.Path // clean, decoded path\nauthorize, err := url.JoinPath(prefix, \"/authorize\")","handlingStrategy":"validation","validationCode":"func checkEndpoints(prefix string) error {\n\tfor _, ep := range []string{\"/jwks.json\", \"/.well-known/openid-configuration\", \"/authorize\"} {\n\t\tif _, err := url.JoinPath(prefix, ep); err != nil {\n\t\t\treturn fmt.Errorf(\"prefix %q invalid for %s: %w\", prefix, ep, err)\n\t\t}\n\t}\n\treturn nil\n}","typeGuard":null,"tryCatchPattern":"if _, err := oidc.NewServer(opts); err != nil {\n    var joinErr *url.Error // JoinPath errors surface via url.Parse\n    if errors.As(err, &joinErr) || strings.Contains(err.Error(), \"join path\") {\n        log.Fatalf(\"fix the issuer path prefix in config: %v\", err)\n    }\n    return err\n}","preventionTips":["Never hand-concatenate prefix strings; parse the issuer once and reuse u.Path","Reject prefixes containing '%' unless produced by url.EscapedPath","Fail fast in main() on config validation before any listener starts"],"tags":["go","dapr","sentry","oidc","url","validation","startup"],"backgroundTag":null,"analyzedSha":"74ad41702745709bb15fe2114ff693b8c59bc3cc","analyzedAt":"2026-08-16T04:22:26.543Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}