fatedier/frp · error · configmgmt.ErrNotFound

not found: proxy %q

Error message

not found: proxy %q

What it means

Returned by GetStoreProxy when a store source exists but contains no proxy with the requested name (storeSource.GetProxy(name) returned nil). It wraps configmgmt.ErrNotFound, which the admin API surfaces as HTTP 404. Only proxies persisted in the config store are visible here; running-but-unstored proxies are not.

Source

Thrown at client/config_manager.go:173

	if err != nil {
		return nil, err
	}
	return storeSource.GetAllProxies()
}

func (m *serviceConfigManager) GetStoreProxy(name string) (v1.ProxyConfigurer, error) {
	if name == "" {
		return nil, fmt.Errorf("%w: proxy name is required", configmgmt.ErrInvalidArgument)
	}

	storeSource, err := m.storeSourceOrError()
	if err != nil {
		return nil, err
	}

	cfg := storeSource.GetProxy(name)
	if cfg == nil {
		return nil, fmt.Errorf("%w: proxy %q", configmgmt.ErrNotFound, name)
	}
	return cfg, nil
}

func (m *serviceConfigManager) CreateStoreProxy(cfg v1.ProxyConfigurer) (v1.ProxyConfigurer, error) {
	if err := m.validateStoreProxyConfigurer(cfg); err != nil {
		return nil, fmt.Errorf("%w: validation error: %v", configmgmt.ErrInvalidArgument, err)
	}

	name := cfg.GetBaseConfig().Name
	persisted, err := m.withStoreProxyMutationAndReload(name, func(storeSource *source.StoreSource) error {
		if err := storeSource.AddProxy(cfg); err != nil {
			if errors.Is(err, source.ErrAlreadyExists) {
				return fmt.Errorf("%w: %v", configmgmt.ErrConflict, err)
			}
			return err
		}
		return nil

View on GitHub (pinned to 6c8a8d0a97)

Solutions

  1. List all stored proxies with GET /api/store/proxies and use the exact returned name.
  2. If the proxy should exist, create it with POST /api/store/proxies first.
  3. Verify you are querying the right frpc instance's admin port; each client has its own store.

Example fix

# before
curl http://127.0.0.1:7400/api/store/proxies/SSH   # 404 not found: proxy "SSH"

# after
curl http://127.0.0.1:7400/api/store/proxies | jq '.[].name'   # find exact name
curl http://127.0.0.1:7400/api/store/proxies/ssh
Defensive patterns

Strategy: try-catch

Validate before calling

// Check existence via the list endpoint first.
func exists(base, name string) bool {
    resp, _ := http.Get(base + "/api/store/proxies")
    defer resp.Body.Close()
    var list []struct{ Name string }
    json.NewDecoder(resp.Body).Decode(&list)
    for _, p := range list { if p.Name == name { return true } }
    return false
}

Try / catch

cfg, err := mgr.GetStoreProxy(name)
if err != nil {
    if errors.Is(err, configmgmt.ErrNotFound) {
        // handle absent proxy: create it or report cleanly
    }
    return err
}

Prevention

When it happens

Trigger: GET /api/store/proxies/{name} for a name that was never created via POST /api/store/proxies, was deleted, or differs in case/spelling from the stored name. Also when querying a proxy defined in the static config file while the store source tracks only API-created entries, depending on how the store source is backed.

Common situations: Case mismatch (MyProxy vs myproxy); assuming file-defined proxies are in the store; a name copied from the frps dashboard that differs from the frpc-side proxy name; checking existence right after deletion.

Related errors


AI-assisted analysis of fatedier/frp@6c8a8d0a97 (2026-08-15). Data as JSON: /api/errors/65b1f5f34b1d7d36. Report an issue: GitHub.