phalcon/cphalcon · error · Phalcon\ADR\Exceptions\HeadersAlreadySent
Headers have already been sent; cannot emit the response.
Error message
Headers have already been sent; cannot emit the response.
What it means
The cursor-based paginator Phalcon\Paginator\Adapter\QueryBuilderCursor validates its constructor config and requires a 'limit' key (page size). If the config array has no 'limit' entry, MissingRequiredParameter('limit') is thrown immediately at construction time.
Source
Thrown at phalcon/ADR/Emitter/SapiEmitter.zep:29
* @link https://pmjones.io/adr/
*/
namespace Phalcon\ADR\Emitter;
use Phalcon\ADR\Exceptions\HeadersAlreadySent;
use Phalcon\Contracts\ADR\Emitter\Emitter;
use Phalcon\Http\ResponseInterface;
/**
* Emits a response through the SAPI (headers + body via `Response::send()`).
* Refuses to emit once headers have already been sent.
*/
class SapiEmitter implements Emitter
{
public function emit(<ResponseInterface> response) -> void
{
if headers_sent() {
throw new HeadersAlreadySent();
}
response->send();
}
}
View on GitHub (pinned to b7419de9cd)
Solutions
- Add 'limit' => <int> to the constructor config array.
- Check the other required keys in the same pass: 'builder' (a Phalcon\Mvc\Model\Query\Builder instance) and 'cursorColumn' (non-empty string) follow immediately after this check.
- Build the config array from a single validated source so all required keys are present together.
Example fix
// before
$paginator = new QueryBuilderCursor(
[
'builder' => $builder,
'cursorColumn' => 'id',
]
); // throws MissingRequiredParameter('limit')
// after
$paginator = new QueryBuilderCursor(
[
'limit' => 20,
'builder' => $builder,
'cursorColumn' => 'id',
]
); Defensive patterns
Strategy: validation
Validate before calling
$required = ['limit', 'builder', 'cursorColumn'];
foreach ($required as $key) {
if (!array_key_exists($key, $config)) {
throw new \InvalidArgumentException("QueryBuilderCursor config lacks '{$key}'");
}
}
$paginator = new \Phalcon\Paginator\Adapter\QueryBuilderCursor($config); Prevention
- Centralize paginator config building in one helper so required keys are never forgotten.
- Write a construction smoke test for each adapter covering the exact config keys you use.
When it happens
Trigger: new QueryBuilderCursor(['builder' => $builder, 'cursorColumn' => 'id']) — the 'limit' key is absent.
Common situations: Migrating config arrays from the offset-based QueryBuilder adapter and dropping a key; typos like 'Limit' or 'pageSize'; introducing the cursor adapter in a framework upgrade where the old config no longer matches.
Understand the failure class
Background: Missing required parameter errors: what 'X is required' and 'the required X param is missing' mean, and how to fix them — this error's family across 27 libraries.
Related errors
- No route matched the request.
- The request method is not allowed for the matched route.
- Invalid value for the accessList
- No action directory set; call setActionDirectory().
- Role '{roleName}' (to inherit) does not exist in the role li
AI-assisted analysis of phalcon/cphalcon@b7419de9cd (2026-08-21).
Data as JSON: /api/errors/75235ef1b7a6e0b6.
Report an issue: GitHub.