{"record":{"id":"2293408f386e9c1e","repo":"walkor/workerman","slug":"request-setsidcookie-fail-header-already-send","errorCode":null,"errorMessage":"Request->setSidCookie() fail, header already send","messagePattern":"Request->setSidCookie\\(\\) fail, header already send","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"src/Protocols/Http/Request.php","lineNumber":738,"sourceCode":"    public static function createSessionId(): string\n    {\n        $sid = session_create_id();\n        if ($sid === false) {\n            throw new RuntimeException('session_create_id() failed');\n        }\n        return $sid;\n    }\n\n    /**\n     * @param string $sessionName\n     * @param string $sid\n     * @param array $cookieParams\n     * @return void\n     */\n    protected function setSidCookie(string $sessionName, string $sid, array $cookieParams): void\n    {\n        if (!$this->connection) {\n            throw new RuntimeException('Request->setSidCookie() fail, header already send');\n        }\n        $this->connection->headers['Set-Cookie'] = [$sessionName . '=' . $sid\n            . (empty($cookieParams['domain']) ? '' : '; Domain=' . $cookieParams['domain'])\n            . (empty($cookieParams['lifetime']) ? '' : '; Max-Age=' . $cookieParams['lifetime'])\n            . (empty($cookieParams['path']) ? '' : '; Path=' . $cookieParams['path'])\n            . (empty($cookieParams['samesite']) ? '' : '; SameSite=' . $cookieParams['samesite'])\n            . (!$cookieParams['secure'] ? '' : '; Secure')\n            . (!$cookieParams['httponly'] ? '' : '; HttpOnly')];\n    }\n\n    /**\n     * __toString.\n     */\n    public function __toString(): string\n    {\n        return $this->buffer;\n    }\n","sourceCodeStart":720,"sourceCodeEnd":756,"githubUrl":"https://github.com/walkor/workerman/blob/1391112a61d23020e11e7b89f17050f6cfaea431/src/Protocols/Http/Request.php#L720-L756","documentation":"setSidCookie() writes the Set-Cookie header onto $this->connection->headers. When the Request no longer has a connection (headers already sent or the request object was detached for reuse in a background context) there is nowhere to put the cookie, so Workerman throws.","triggerScenarios":"The session-id path reaching setSidCookie() on a Request whose connection property is null: response already sent before session()/sessionId() ran, or the Request was captured and processed later (Timer/queue) after the HTTP exchange finished.","commonSituations":"Same lifecycle mistakes as the sibling 'Request->session() fail, header already send' error: early response send followed by session writes, or request objects handed to deferred jobs.","solutions":["Touch the session (and any setSidCookie-triggering call) before sending the response","Pass the session id string to background jobs instead of the whole Request object","Verify client sends a valid session cookie on subsequent requests so no new cookie must be emitted"],"exampleFix":"// before\n$connection->send(new Response(200, [], 'ok'));\n$request->sessionId($newId); // needs Set-Cookie -> throws\n\n// after\n$request->sessionId($newId);\n$connection->send(new Response(200, [], 'ok'));","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"function canSendCookie(Workerman\\Protocols\\Http\\Request $request): bool\n{\n    return $request->connection !== null;\n}","tryCatchPattern":null,"preventionTips":["Do all session-id/cookie work before the first send() on the connection","Treat Request as single-request-scoped; never reuse it across ticks, processes, or jobs"],"tags":["php","workerman","http","session","cookies"],"backgroundTag":"headers-already-sent","analyzedSha":"1391112a61d23020e11e7b89f17050f6cfaea431","analyzedAt":"2026-08-21T02:05:46.744Z","schemaVersion":2},"datasetVersion":"2026-08-21T03:17:12.404Z"}