chenhg5/cc-connect · error

prompt and exec are mutually exclusive

Error message

prompt and exec are mutually exclusive

What it means

The request contained both `prompt` and `exec`, which are mutually exclusive actions; the handler returns 400 "prompt and exec are mutually exclusive". A single webhook call can either send a natural-language prompt or execute a command, not both.

Source

Thrown at core/webhook.go:112

		return
	}

	var req WebhookRequest
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		http.Error(w, "invalid JSON: "+err.Error(), http.StatusBadRequest)
		return
	}

	if req.SessionKey == "" {
		http.Error(w, "session_key is required", http.StatusBadRequest)
		return
	}
	if req.Prompt == "" && req.Exec == "" {
		http.Error(w, "either prompt or exec is required", http.StatusBadRequest)
		return
	}
	if req.Prompt != "" && req.Exec != "" {
		http.Error(w, "prompt and exec are mutually exclusive", http.StatusBadRequest)
		return
	}

	engine, err := ws.resolveEngine(req.Project)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	eventName := req.Event
	if eventName == "" {
		eventName = "webhook"
	}

	slog.Info("webhook: received",
		"event", eventName,
		"project", req.Project,
		"session_key", req.SessionKey,

View on GitHub (pinned to 4000b2338a)

Solutions

  1. Remove one of the two fields — send prompt OR exec in a single request.
  2. Issue two separate webhook calls if both actions are needed.
  3. In the payload builder, zero out the unused field before marshaling.
  4. Apply omitempty / explicit emptiness checks so defaults don't leak both fields.

Example fix

// before
{"session_key":"s1","prompt":"fix tests","exec":"go test"}
// after
{"session_key":"s1","prompt":"run go test and fix failures"}
Defensive patterns

Strategy: validation

Validate before calling

func checkWebhookReq(r WebhookRequest) error {
    if r.Prompt != "" && r.Exec != "" { return errors.New("prompt and exec are mutually exclusive") }
    return nil
}

Type guard

func exactlyOneAction(p map[string]any) bool {
    pr, _ := p["prompt"].(string); ex, _ := p["exec"].(string)
    return (pr != "") != (ex != "")
}

Try / catch

resp, err := http.Post(url, "application/json", bytes.NewReader(payload))
if err == nil && resp.StatusCode == 400 {
    b, _ := io.ReadAll(resp.Body)
    if strings.Contains(string(b), "mutually exclusive") {
        return errors.New("drop either prompt or exec and resend")
    }
    return fmt.Errorf("webhook rejected: %s", b)
}

Prevention

When it happens

Trigger: JSON body where both fields are non-empty strings, e.g. {"prompt":"fix tests","exec":"go test"}; automation templates that always serialize both keys including defaults.

Common situations: Payload builders marshaling a struct with both fields set to defaults; merging of two configs/requests; user pasting a command into the prompt UI while the automation also fills exec.

Related errors


AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06). Data as JSON: /api/errors/122b93f4460cc7e0. Report an issue: GitHub.