router-for-me/CLIProxyAPI · error

plugin store resolved auth header value is empty

Error message

plugin store resolved auth header value is empty

What it means

Thrown while applying a header-type plugin-store auth rule: the header value resolved to an empty byte slice. internal/pluginstore/auth.go checks len(item.HeaderValue) == 0 before calling headers.Set, because sending a named header with an empty value would produce a guaranteed-401 request. The value usually comes from an environment variable (header-value-env), so an unset or blank env var is the typical root cause.

Source

Thrown at internal/pluginstore/auth.go:301

	case AuthTypeBasic:
		if len(item.Username) == 0 || len(item.Password) == 0 {
			return false, fmt.Errorf("plugin store resolved basic auth is incomplete")
		}
		credential := make([]byte, 0, len(item.Username)+1+len(item.Password))
		credential = append(credential, item.Username...)
		credential = append(credential, ':')
		credential = append(credential, item.Password...)
		encoded := base64.StdEncoding.EncodeToString(credential)
		for index := range credential {
			credential[index] = 0
		}
		headers.Set("Authorization", "Basic "+encoded)
	case AuthTypeHeader:
		if strings.TrimSpace(item.HeaderName) == "" {
			return false, fmt.Errorf("plugin store resolved auth missing header-name")
		}
		if len(item.HeaderValue) == 0 {
			return false, fmt.Errorf("plugin store resolved auth header value is empty")
		}
		headers.Set(item.HeaderName, string(item.HeaderValue))
	default:
		return false, fmt.Errorf("unsupported plugin store resolved auth type %q", item.Type)
	}
	return true, nil
}

func validatePluginStoreRequestURL(auth []AuthConfig, requestURL string, kind string) error {
	parsed, errParse := url.Parse(strings.TrimSpace(requestURL))
	if errParse != nil || parsed.Scheme == "" || parsed.Host == "" {
		return fmt.Errorf("invalid plugin store url")
	}
	if parsed.User != nil {
		return fmt.Errorf("plugin store url must not contain credentials")
	}
	if hasSensitiveQueryParameter(parsed) {
		return fmt.Errorf("plugin store url contains sensitive query parameter")

View on GitHub (pinned to 78f0c4079e)

Solutions

  1. Set the referenced env var to the real token value in the environment that runs the server (export it, add it to .env, or the systemd/docker env)
  2. Confirm the header-value-env name in config.yaml matches the variable name exactly (case-sensitive)
  3. Check for whitespace-only values — the resolver trims and yields an empty Secret
  4. Restart the process so the env change is picked up

Example fix

# before (.env)
# PLUGIN_API_KEY=  (empty)

# after (.env)
PLUGIN_API_KEY=actual-secret-value
Defensive patterns

Strategy: validation

Validate before calling

if strings.EqualFold(strings.TrimSpace(rule.Type), "header") {
    v := strings.TrimSpace(os.Getenv(rule.HeaderValueEnv))
    if v == "" {
        return fmt.Errorf("env %s unset; will fail auth resolution", rule.HeaderValueEnv)
    }
}

Prevention

When it happens

Trigger: A header-type auth rule matches the request URL and kind, but the env var named by header-value-env is unset, empty, or whitespace that was trimmed to zero bytes during resolution.

Common situations: Env var not exported in the shell/service unit that runs CLIProxyAPI; .env file missing the key; secret rotated to an empty value; deploy environment (container/systemd) differs from the developer shell where the variable exists.

Related errors


AI-assisted analysis of router-for-me/CLIProxyAPI@78f0c4079e (2026-08-15). Data as JSON: /api/errors/cbed9e2715fb131e. Report an issue: GitHub.