router-for-me/CLIProxyAPI · error

json is required

Error message

json is required

What it means

Thrown by validateHostAuthSaveRequest when the request's JSON payload trims to zero length. The save callback requires a body; an empty (or whitespace-only) JSON field is rejected before any filesystem action.

Source

Thrown at internal/pluginhost/auth_callbacks.go:272

	}
	var metadata map[string]any
	if errUnmarshal := json.Unmarshal(data, &metadata); errUnmarshal != nil {
		return nil, nil, fmt.Errorf("invalid auth file for auth_index %s: %w", authIndex, errUnmarshal)
	}
	return auth, data, nil
}

func validateHostAuthSaveRequest(req pluginapi.HostAuthSaveRequest) (string, []byte, error) {
	name := strings.TrimSpace(req.Name)
	if isUnsafeAuthFileName(name) {
		return "", nil, fmt.Errorf("invalid auth file name")
	}
	if !strings.HasSuffix(strings.ToLower(name), ".json") {
		return "", nil, fmt.Errorf("auth file name must end with .json")
	}
	rawJSON := bytesTrimSpace(req.JSON)
	if len(rawJSON) == 0 {
		return "", nil, fmt.Errorf("json is required")
	}
	var metadata map[string]any
	if errUnmarshal := json.Unmarshal(rawJSON, &metadata); errUnmarshal != nil {
		return "", nil, fmt.Errorf("invalid auth json: %w", errUnmarshal)
	}
	return filepath.Base(name), rawJSON, nil
}

func (h *Host) saveAuthFile(ctx context.Context, name string, data []byte) (string, error) {
	authDir := h.resolvedAuthDir()
	if authDir == "" {
		return "", fmt.Errorf("auth directory is unavailable")
	}
	dst := filepath.Join(authDir, filepath.Base(name))
	if !filepath.IsAbs(dst) {
		if abs, errAbs := filepath.Abs(dst); errAbs == nil {
			dst = abs
		}

View on GitHub (pinned to 78f0c4079e)

Solutions

  1. Marshal the auth object and assign it to req.JSON before calling save
  2. Add a pre-check in the plugin: len(bytes.TrimSpace(json)) > 0
  3. Log the request (minus secrets) when save fails to catch empty payloads early

Example fix

// before
req := pluginapi.HostAuthSaveRequest{Name: name, JSON: string(data)} // data empty

// after
if len(bytes.TrimSpace(data)) == 0 { return errors.New("nothing to save") }
req := pluginapi.HostAuthSaveRequest{Name: name, JSON: string(data)}
Defensive patterns

Strategy: validation

Validate before calling

if len(bytes.TrimSpace(req.JSON)) == 0 {
    return errors.New("refusing to save empty auth json")
}

Prevention

When it happens

Trigger: Plugin sends HostAuthSaveRequest{Name: "x.json", JSON: ""} or JSON containing only whitespace/newlines.

Common situations: Plugin bug where the marshaled struct is forgotten (zero value sent); serialization step silently produced empty output; a test stub omitting the field.

Related errors


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