JuliusBrussee/caveman · error
repeated path separators are not allowed
Error message
repeated path separators are not allowed
What it means
validatePathComponents rejects empty path segments in the middle of a path (i.e. '//'), while allowing the leading and trailing empty segments of '/compat/x/' style paths. Repeated separators can bypass prefix matching or be collapsed differently by intermediate proxies, changing route identity.
Solutions
- Remove the empty segment(s): collapse '//' to '/' in the request path
- Fix base/path string concatenation to avoid a double slash
- Clean the configured BaseURL so its path has no '//'
- Use url.JoinPath or the adapter's joinCompatPath instead of manual concatenation
Example fix
// before GET /compat/openai//v1/chat/completions // after GET /compat/openai/v1/chat/completions
Defensive patterns
Strategy: validation
Validate before calling
u, err := url.Parse(rawURL)
if err == nil && strings.Contains(strings.TrimPrefix(u.Path, "/"), "//") {
return errors.New("double slash in path")
} Try / catch
if err := ValidateBaseURL(base); err != nil {
return fmt.Errorf("bad upstream base url: %w", err)
} Prevention
- Join paths with url.JoinPath instead of string concatenation
- Strip trailing slashes from configured base URLs before joining
When it happens
Trigger: Requests like /compat/openai//v1/chat/completions; base URL config strings with double slashes in the path portion; clients concatenating base and suffix paths carelessly producing 'a//b'.
Common situations: String-joining a base URL ending in '/' with a path starting with '/'; proxies that normalized then re-emitted double slashes; config typos.
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
- backslash is not allowed in path
- ambiguous escaped path sequence
- compat request URL is missing
- compat route path rejected
- compat route does not match prefix
AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20).
Data as JSON: /api/errors/dffc6dc82918195d.
Report an issue: GitHub.
Appendix: source
Thrown at proxy/providers/openaicompat/openaicompat.go:494
func validateCompatPath(path, rawPath string) error {
if !strings.HasPrefix(path, "/compat/") {
return nil
}
if err := validatePathComponents(path, rawPath); err != nil {
return fmt.Errorf("compat route path rejected: %w", err)
}
return nil
}
func validatePathComponents(path, rawPath string) error {
if strings.Contains(path, `\`) {
return fmt.Errorf("backslash is not allowed in path")
}
segments := strings.Split(path, "/")
for i, segment := range segments {
if segment == "" && i > 0 && i < len(segments)-1 {
return fmt.Errorf("repeated path separators are not allowed")
}
if segment == "." || segment == ".." {
return fmt.Errorf("dot segments are not allowed in path")
}
}
// URL.Path is decoded by net/url while RawPath retains a valid escaped
// spelling. Reject separators, backslashes, and dot bytes in either form so
// a path cannot change route identity after another decoder or proxy hop.
for _, escape := range []string{"%2f", "%5c", "%2e"} {
if strings.Contains(strings.ToLower(path), escape) || strings.Contains(strings.ToLower(rawPath), escape) {
return fmt.Errorf("ambiguous escaped path sequence %s", escape)
}
}
return nil
}
func parseBaseURL(raw, provider string) (*url.URL, error) {
if strings.TrimSpace(raw) == "" {View on GitHub (pinned to 3ee70a1026)