{"record":{"id":"72450bea05ce76ee","repo":"chenhg5/cc-connect","slug":"acp-session-new-empty-sessionid","errorCode":null,"errorMessage":"acp: session/new: empty sessionId","messagePattern":"acp: session/new: empty sessionId","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"agent/acp/session.go","lineNumber":246,"sourceCode":"\t}\n\n\tnewParams := map[string]any{\n\t\t\"cwd\":        s.workDir,\n\t\t\"mcpServers\": []any{},\n\t}\n\tnewRes, err := s.tr.call(s.ctx, \"session/new\", newParams)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"acp: session/new: %w\", err)\n\t}\n\tvar sn struct {\n\t\tSessionID string         `json:\"sessionId\"`\n\t\tModes     *acpModesBlock `json:\"modes\"`\n\t}\n\tif err := json.Unmarshal(newRes, &sn); err != nil {\n\t\treturn fmt.Errorf(\"acp: parse session/new: %w\", err)\n\t}\n\tif sn.SessionID == \"\" {\n\t\treturn fmt.Errorf(\"acp: session/new: empty sessionId\")\n\t}\n\ts.setACPSessionID(sn.SessionID)\n\ts.absorbModes(sn.Modes)\n\treturn nil\n}\n\n// absorbModes copies a modes block into the session's cache and fans\n// it out to the parent agent callbacks (if any). Both the session and\n// the agent need the information: the session uses it to validate\n// SetLiveMode inputs; the agent uses it to render `/mode` menus in IM.\nfunc (s *acpSession) absorbModes(block *acpModesBlock) {\n\tif block == nil || len(block.AvailableModes) == 0 {\n\t\treturn\n\t}\n\ts.modesMu.Lock()\n\ts.availableModes = append(s.availableModes[:0], block.AvailableModes...)\n\tif block.CurrentModeID != \"\" {\n\t\ts.currentMode = block.CurrentModeID","sourceCodeStart":228,"sourceCodeEnd":264,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/agent/acp/session.go#L228-L264","documentation":"Raised when the `session/new` call succeeded and parsed, but the returned sessionId field is empty. An ACP session without an id is unusable (session/prompt needs it), so the handshake is treated as failed.","triggerScenarios":"The agent's session/new result contains no `sessionId` key or an empty string value — typically a non-conforming agent or a server that reports success while failing to actually create the session.","commonSituations":"Agent bug or incomplete ACP implementation returning an empty id; a shim returning {\"sessionId\": \"\"} on internal error; version mismatch where the agent creates sessions lazily and does not return ids.","solutions":["Upgrade the agent to a version with a conforming session/new implementation.","Check agent logs/stderr for an internal error that made it return an empty id.","Test the agent directly with an ACP client (e.g. Zed) to confirm it returns a real sessionId.","If a wrapper/shim produces the response, fix it to propagate the agent's actual session id."],"exampleFix":"// non-conforming agent response\n// {\"sessionId\": \"\"}\n// after: agent returns\n// {\"sessionId\": \"sess_01H...\", \"modes\": {\"currentModeId\": \"code\"}}","handlingStrategy":"validation","validationCode":"var probe struct{ SessionID string `json:\"sessionId\"` }\nif err := json.Unmarshal(newRes, &probe); err == nil && probe.SessionID == \"\" {\n    return fmt.Errorf(\"agent returned empty sessionId\")\n}","typeGuard":null,"tryCatchPattern":"sess, err := agent.StartSession(ctx, id, nil)\nif err != nil && strings.Contains(err.Error(), \"empty sessionId\") {\n    slog.Error(\"agent violates ACP session/new contract — upgrade agent\", \"err\", err)\n    return err\n}","preventionTips":["Test agents against the ACP spec (or Zed) before wiring them into cc-connect.","Upgrade agents with known session/new bugs.","Verify with a raw JSON-RPC probe that session/new yields a non-empty sessionId."],"tags":["acp","handshake","session","protocol-violation"],"backgroundTag":"empty-required-field","analyzedSha":"4000b2338aa6e850c99df54f8b0ed6ed7460b401","analyzedAt":"2026-09-06T11:45:09.575Z","contentChangedAt":"2026-09-06T11:45:09.575Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}