fatedier/frp · error · ErrInvalidArgument

invalid argument: body can't be empty

Error message

invalid argument: body can't be empty

What it means

Returned by WriteConfigFile (PUT /api/config on the frpc admin API) when the request body is empty. frpc refuses zero-length writes so an empty body cannot truncate and destroy the live config file. It is a pure request-construction error on the caller side.

Source

Thrown at client/config_manager.go:70

	log.Infof("success reload conf")
	return nil
}

func (m *serviceConfigManager) ReadConfigFile() (string, error) {
	if m.svr.configFilePath == "" {
		return "", fmt.Errorf("%w: frpc has no config file path", configmgmt.ErrInvalidArgument)
	}

	content, err := os.ReadFile(m.svr.configFilePath)
	if err != nil {
		return "", fmt.Errorf("%w: %v", configmgmt.ErrInvalidArgument, err)
	}
	return string(content), nil
}

func (m *serviceConfigManager) WriteConfigFile(content []byte) error {
	if len(content) == 0 {
		return fmt.Errorf("%w: body can't be empty", configmgmt.ErrInvalidArgument)
	}

	if err := os.WriteFile(m.svr.configFilePath, content, 0o600); err != nil {
		return err
	}
	return nil
}

func (m *serviceConfigManager) GetProxyStatus() []*proxy.WorkingStatus {
	return m.svr.getAllProxyStatus()
}

func (m *serviceConfigManager) GetProxyConfig(name string) (v1.ProxyConfigurer, bool) {
	// Try running proxy manager first
	ws, ok := m.svr.getProxyStatus(name)
	if ok {
		return ws.Cfg, true
	}

View on GitHub (pinned to 6c8a8d0a97)

Solutions

  1. Send the complete config file content as the PUT body: curl -X PUT --data-binary @frpc.toml http://127.0.0.1:7400/api/config.
  2. If your tooling produced an empty body, fix the template/render step so it emits at least a minimal valid config.
  3. Verify body length before sending (e.g. test -s frpc.toml) in scripts.

Example fix

# before
curl -X PUT http://127.0.0.1:7400/api/config   # 400 body can't be empty

# after
curl -X PUT --data-binary @frpc.toml http://127.0.0.1:7400/api/config
Defensive patterns

Strategy: validation

Validate before calling

// Never send an empty body to PUT /api/config.
if len(content) == 0 {
    return errors.New("refusing to PUT empty config")
}
req, _ := http.NewRequest(http.MethodPut, base+"/api/config", bytes.NewReader(content))

Prevention

When it happens

Trigger: PUT /api/config with an empty body, a body of only whitespace is fine but zero bytes is not; curl invoked without -d/--data-binary so no body is sent; a CI job whose config template rendered to an empty string.

Common situations: Automation that renders a TOML template and PUTs it while the template variables are all empty, yielding a zero-byte payload; curl commands missing --data-binary @file; HTTP clients that drop the body on redirects.

Related errors


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