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
- Remove one of the two fields — send prompt OR exec in a single request.
- Issue two separate webhook calls if both actions are needed.
- In the payload builder, zero out the unused field before marshaling.
- 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
- Model the payload as prompt XOR exec at the type level where possible
- Zero out the unused field before marshaling
- Combine both intents into one prompt instead of two fields
- Add a unit test asserting builders never emit both fields
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
- invalid JSON:
- session_key is required
- either prompt or exec is required
- err.Error()
- app_id/app_secret are required
AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06).
Data as JSON: /api/errors/122b93f4460cc7e0.
Report an issue: GitHub.