fatedier/frp · error
missing %s
Error message
missing %s
What it means
Produced by decodeV2PathParam when the requested path parameter (e.g. "key" for /api/v2/clients/{key}) is an empty string. The route matched but the variable segment is missing, so no URL-decoding is attempted. Because it is created with fmt.Errorf rather than httppkg.NewError, it surfaces as an internal-style error instead of a clean 400.
Source
Thrown at server/http/controller_v2.go:300
// /api/v2/proxies/{name}/traffic
func (c *Controller) APIV2ProxyTraffic(ctx *httppkg.Context) (any, error) {
name, err := decodeV2PathParam(ctx, "name", "proxy name")
if err != nil {
return nil, err
}
proxyTrafficInfo := mem.StatsCollector.GetProxyTraffic(name)
if proxyTrafficInfo == nil {
return nil, httppkg.NewError(http.StatusNotFound, "no proxy info found")
}
return buildV2ProxyTrafficResp(name, proxyTrafficInfo, time.Now()), nil
}
func decodeV2PathParam(ctx *httppkg.Context, key string, label string) (string, error) {
raw := ctx.Param(key)
if raw == "" {
return "", fmt.Errorf("missing %s", label)
}
decoded, err := url.PathUnescape(raw)
if err != nil {
return "", httppkg.NewError(http.StatusBadRequest, fmt.Sprintf("invalid %s", label))
}
return decoded, nil
}
func getOrCreateV2User(items map[string]*model.V2UserResp, user string) *model.V2UserResp {
item, ok := items[user]
if !ok {
item = &model.V2UserResp{User: user}
items[user] = item
}
return item
}
func parseV2PageParams(ctx *httppkg.Context) (int, int, error) {View on GitHub (pinned to 6c8a8d0a97)
Solutions
- Include the client key in the path: GET /api/v2/clients/{actual-key}
- Check reverse-proxy rewrite rules if the caller sends the key but the server sees it empty
- In scripts, assert the key variable is non-empty before issuing the request
Example fix
# before curl http://frps:7500/api/v2/clients/ # after curl http://frps:7500/api/v2/clients/abc123
Defensive patterns
Strategy: validation
Validate before calling
key := os.Getenv("CLIENT_KEY")
if strings.TrimSpace(key) == "" {
log.Fatal("CLIENT_KEY must not be empty")
}
url := fmt.Sprintf("/api/v2/clients/%s", url.PathEscape(key)) Prevention
- Always URL-encode and non-empty-check path parameters before building v2 API URLs
- Assert on the shape of the final URL (no empty segments) in integration tests
- Watch reverse-proxy rewrite rules that can strip the last path segment
When it happens
Trigger: Requesting /api/v2/clients/ (trailing slash, empty key) or /api/v2/clients//status; a route rewriter or reverse proxy stripping the path segment; calling the router with a mismatched route pattern that leaves the param unset.
Common situations: Manually building dashboard/API URLs and forgetting the key; gateway rewrite rules (nginx traefik) that trim the last path segment; scripts concatenating an empty variable into the URL.
Related errors
- subdomain is not supported because this feature is not enabl
- '.' and '*' are not supported in subdomain
- envelope.msg
- invalid argument: visitor name in URL must match name in bod
- proxy name is required
AI-assisted analysis of fatedier/frp@6c8a8d0a97 (2026-08-15).
Data as JSON: /api/errors/ea230da3418d2575.
Report an issue: GitHub.