phacility/phabricator · error · Exception
Call %s when rendering an action as a form.
Error message
Call %s when rendering an action as a form.
What it means
When a PhabricatorActionView renders as a form (setRenderAsForm(true) with an href), it emits a real form whose CSRF/session token derives from the viewer, so render() requires one and throws if none is set. PhabricatorActionListView propagates its own viewer to every action at render time (it calls $action->setViewer($viewer)), so actions inside a viewer-configured list never hit this; standalone actions or viewer-less lists do.
Source
Thrown at src/view/layout/PhabricatorActionView.php:234
if ($this->download) {
$sigils[] = 'download';
}
if ($this->submenu) {
$sigils[] = 'keep-open';
}
if ($this->sigils) {
$sigils = array_merge($sigils, $this->sigils);
}
$sigils = $sigils ? implode(' ', $sigils) : null;
if ($this->href) {
if ($this->renderAsForm) {
if (!$this->hasViewer()) {
throw new Exception(
pht(
'Call %s when rendering an action as a form.',
'setViewer()'));
}
$item = javelin_tag(
'button',
array(
'class' => 'phabricator-action-view-item',
),
array($icon, $this->name));
$item = phabricator_form(
$this->getViewer(),
array(
'action' => $this->getHref(),
'method' => 'POST',
'sigil' => $sigils,View on GitHub (pinned to 5720a38cfe)
Solutions
- Call $action_view->setViewer($viewer) directly on the action.
- Or attach actions to a PhabricatorActionListView and call $list->setViewer($viewer) once; the list propagates it to each action at render time.
- In controllers, always source the viewer from $this->getViewer().
Example fix
// before
$action = id(new PhabricatorActionView())
->setHref('/obj/subscribe/')
->setRenderAsForm(true)
->setName(pht('Subscribe'));
// no viewer: render() throws
// after
$action = id(new PhabricatorActionView())
->setViewer($viewer)
->setHref('/obj/subscribe/')
->setRenderAsForm(true)
->setName(pht('Subscribe')); Defensive patterns
Strategy: validation
Validate before calling
if (!$action->hasViewer()) {
$action->setViewer($viewer);
}
// and for lists (propagates to every action on render):
$list->setViewer($viewer); Prevention
- Call setViewer() on the PhabricatorActionListView once instead of per action.
- Any action with setRenderAsForm(true) implies a viewer is required.
- hasViewer() is public — assert it before rendering standalone actions.
When it happens
Trigger: Creating a standalone PhabricatorActionView with setRenderAsForm(true) (and an href) and rendering it without setViewer(); or rendering an action list on which setViewer() was never called.
Common situations: Custom action lists built in controllers where $list->setViewer($viewer) was forgotten; embedding a single form-action in a hovercard or curtain; refactors that pull an action out of a list into standalone rendering.
Related errors
- You must pass the user to %s.
- Call %s before rendering tokenizers. Use %s on %s to do this
- Service "%s" is unrecognized, restricted, or you do not have
- When creating a new Almanac interface via the Conduit API, y
- Device "%s" is unrecognized, restricted, or you do not have
AI-assisted analysis of phacility/phabricator@5720a38cfe (2026-08-21).
Data as JSON: /api/errors/886db918a23b50ba.
Report an issue: GitHub.