walkor/workerman · error · RuntimeException

Request->setSidCookie() fail, header already send

Error message

Request->setSidCookie() fail, header already send

What it means

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.

Source

Thrown at src/Protocols/Http/Request.php:738

    public static function createSessionId(): string
    {
        $sid = session_create_id();
        if ($sid === false) {
            throw new RuntimeException('session_create_id() failed');
        }
        return $sid;
    }

    /**
     * @param string $sessionName
     * @param string $sid
     * @param array $cookieParams
     * @return void
     */
    protected function setSidCookie(string $sessionName, string $sid, array $cookieParams): void
    {
        if (!$this->connection) {
            throw new RuntimeException('Request->setSidCookie() fail, header already send');
        }
        $this->connection->headers['Set-Cookie'] = [$sessionName . '=' . $sid
            . (empty($cookieParams['domain']) ? '' : '; Domain=' . $cookieParams['domain'])
            . (empty($cookieParams['lifetime']) ? '' : '; Max-Age=' . $cookieParams['lifetime'])
            . (empty($cookieParams['path']) ? '' : '; Path=' . $cookieParams['path'])
            . (empty($cookieParams['samesite']) ? '' : '; SameSite=' . $cookieParams['samesite'])
            . (!$cookieParams['secure'] ? '' : '; Secure')
            . (!$cookieParams['httponly'] ? '' : '; HttpOnly')];
    }

    /**
     * __toString.
     */
    public function __toString(): string
    {
        return $this->buffer;
    }

View on GitHub (pinned to 1391112a61)

Solutions

  1. Touch the session (and any setSidCookie-triggering call) before sending the response
  2. Pass the session id string to background jobs instead of the whole Request object
  3. Verify client sends a valid session cookie on subsequent requests so no new cookie must be emitted

Example fix

// before
$connection->send(new Response(200, [], 'ok'));
$request->sessionId($newId); // needs Set-Cookie -> throws

// after
$request->sessionId($newId);
$connection->send(new Response(200, [], 'ok'));
Defensive patterns

Strategy: type-guard

Type guard

function canSendCookie(Workerman\Protocols\Http\Request $request): bool
{
    return $request->connection !== null;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of walkor/workerman@1391112a61 (2026-08-21). Data as JSON: /api/errors/2293408f386e9c1e. Report an issue: GitHub.