walkor/workerman · error · RuntimeException
Request->session() fail, header already send
Error message
Request->session() fail, header already send
What it means
sessionId() must mint a new session id and emit a Set-Cookie header when the request carries no valid session cookie. That is only possible while the Request still owns its connection ($request->connection); once the response/headers have already been sent or the request was detached, no header can be added, so Workerman throws instead of silently creating an unusable session.
Source
Thrown at src/Protocols/Http/Request.php:385
* @return string
* @throws Exception
*/
public function sessionId(?string $sessionId = null): string
{
if ($sessionId) {
unset($this->context['sid'], $this->context['session']);
}
if (!isset($this->context['sid'])) {
$sessionName = Session::$name;
$sid = $sessionId ? '' : $this->cookie($sessionName);
// Strip surrounding double quotes (RFC 6265 allows DQUOTE-wrapped cookie values).
if (is_string($sid) && isset($sid[1]) && $sid[0] === '"' && $sid[-1] === '"') {
$sid = substr($sid, 1, -1);
}
$sid = $this->isValidSessionId($sid) ? $sid : '';
if ($sid === '') {
if (!$this->connection) {
throw new RuntimeException('Request->session() fail, header already send');
}
$sid = $sessionId ?: static::createSessionId();
$cookieParams = Session::getCookieParams();
$this->setSidCookie($sessionName, $sid, $cookieParams);
}
$this->context['sid'] = $sid;
}
return $this->context['sid'];
}
/**
* Check if session id is valid.
*
* @param mixed $sessionId
* @return bool
*/
public function isValidSessionId(mixed $sessionId): bool
{View on GitHub (pinned to 1391112a61)
Solutions
- Open the session before any output: call $request->session() at the top of onMessage, before sending anything on the connection
- Do not use the Request in deferred/background tasks; extract the session id ($request->sessionId()) first and pass that string to the job
- Ensure the client actually receives the PHPSSESSID cookie so later requests have a valid sid and never need the header-write path
Example fix
// before
$connection->send($response);
$session = $request->session(); // throws 'header already send'
// after
$session = $request->session();
$session->set('uid', 1);
$connection->send($response); Defensive patterns
Strategy: type-guard
Type guard
function canCreateSession(Workerman\Protocols\Http\Request $request): bool
{
// A new Set-Cookie can only be emitted while the request still owns its connection.
return $request->connection !== null;
} Try / catch
try {
$session = $request->session();
} catch (RuntimeException $e) {
if (str_contains($e->getMessage(), 'header already send')) {
// headers already sent: log and degrade (no session this request)
return $response->withStatus(500);
}
throw $e;
} Prevention
- First statement of onMessage: open the session; last: send the response
- Never pass Request objects into Timer callbacks or queues; extract $request->sessionId() and pass the string
- In coroutines, capture the session id before the first await and re-attach by id afterwards
When it happens
Trigger: Calling $request->session() (or sessionId(null)) after already sending the response on the connection; using the Request object inside a deferred context (Timer callback, queue consumer, coroutine resumed later) where connection is null; storing the request and touching session after onMessage returned.
Common situations: Code that pushes an early response (e.g. $connection->send('ok')) then continues processing and reads the session; background jobs that receive the whole Request object; middleware that emits headers before session start; workerman v5 coroutine code awaiting something before touching session.
Related errors
- Request->setSidCookie() fail, header already send
- session_create_id() failed
- Please install redis extension.
- Redis connect {$config['host']}:{$config['port']} fail.
- Bad remoteAddress
AI-assisted analysis of walkor/workerman@1391112a61 (2026-08-21).
Data as JSON: /api/errors/084fb68de3b7ed06.
Report an issue: GitHub.