chenhg5/cc-connect · error

session_key is required

Error message

session_key is required

What it means

HTTP 400 validation response from the webhook handler: the authenticated WebhookRequest JSON decoded successfully but its session_key field is empty, so the engine has no session to route the prompt or exec payload to.

Source

Thrown at core/webhook.go:104

func (ws *WebhookServer) handleHook(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodPost {
		http.Error(w, "POST only", http.StatusMethodNotAllowed)
		return
	}

	if !ws.authenticate(r) {
		http.Error(w, "unauthorized", http.StatusUnauthorized)
		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

View on GitHub (pinned to 4000b2338a)

Solutions

  1. Include a non-empty session_key in the JSON body (snake_case).
  2. Use the exact session key shown by the engine/session listing (e.g. /history or /sessions output).
  3. If key naming is the issue, rename sessionId → session_key in the payload.
  4. Client-side, reject payloads with empty session_key before dispatching the request.

Example fix

// before
{"prompt": "run tests"}
// after
{"session_key": "my-project:s1", "prompt": "run tests"}
Defensive patterns

Strategy: validation

Validate before calling

func checkWebhookReq(r WebhookRequest) error {
    if strings.TrimSpace(r.SessionKey) == "" { return errors.New("session_key is required") }
    return nil
}

Type guard

func hasSessionKey(p map[string]any) bool { k, ok := p["session_key"].(string); return ok && strings.TrimSpace(k) != "" }

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), "session_key is required") {
        return errors.New("add non-empty session_key (snake_case) to payload")
    }
    return fmt.Errorf("webhook rejected: %s", b)
}

Prevention

When it happens

Trigger: POSTing a JSON object without the session_key key, or with "session_key": ""; sending a differently-named field (e.g. sessionId or sessionID) that the struct ignores.

Common situations: Client payloads written against an older/renamed API schema; builders omitting optional-looking fields; Go/JS clients using camelCase while the server expects snake_case.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


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