phacility/phabricator · error · PhabricatorWorkerPermanentFailureException
%s has no visible object on the other side; this likely indi
Error message
%s has no visible object on the other side; this likely indicates the Asana task has been deleted.
What it means
After saving the Phabricator-to-Asana edge for the main task, the worker re-checks the imported ref's visibility. If the parent Asana object is not visible (the Asana task was deleted, or the possessed user lost access), PhabricatorWorkerPermanentFailureException is thrown so the daemon stops trying to update a dead target. This is by-design cleanup behavior, not a bug: the edge was already recorded and the story is intentionally dropped.
Source
Thrown at src/applications/doorkeeper/worker/DoorkeeperAsanaFeedWorker.php:248
$dst_phid = $parent_ref->getExternalObject()->getPHID();
// Update the main edge.
$edge_data = array(
'cursor' => $story->getChronologicalKey(),
) + $extra_data;
$edge_options = array(
'data' => $edge_data,
);
id(new PhabricatorEdgeEditor())
->addEdge($src_phid, $etype_main, $dst_phid, $edge_options)
->save();
if (!$parent_ref->getIsVisible()) {
throw new PhabricatorWorkerPermanentFailureException(
pht(
'%s has no visible object on the other side; this '.
'likely indicates the Asana task has been deleted.',
'DoorkeeperExternalObject'));
}
// Now, handle the subtasks.
$sub_editor = new PhabricatorEdgeEditor();
// First, find all the object references in Phabricator for tasks that we
// know about and import their objects from Asana.
$sub_edges = $edges[$src_phid][$etype_sub];
$sub_refs = array();
$subtask_data = $this->getAsanaSubtaskData($object);
$have_phids = array();
if ($sub_edges) {View on GitHub (pinned to 5720a38cfe)
Solutions
- Treat it as expected behavior: the deleted-task story is dropped permanently and no action is needed
- To stop the noise, remove the stale PhabricatorObjectHasAsanaTask edge from the object so new stories stop mirroring
- If the task should still exist, verify it is visible to a user with a linked, working Asana account
Defensive patterns
Strategy: try-catch
Validate before calling
// Before mirroring, check the target Asana object still exists/is visible.
if (!$parent_ref->getIsVisible()) {
// Drop or clean up the edge instead of saving it and letting the worker fail.
return;
} Try / catch
try {
$worker->executeTask();
} catch (PhabricatorWorkerPermanentFailureException $ex) {
// Expected for deleted Asana tasks: acknowledge and stop retrying.
phlog(pht('Asana task gone, dropping story: %s', $ex->getMessage()));
} Prevention
- Remove stale PhabricatorObjectHasAsanaTask edges when the Asana task is known-deleted
- Treat permanent failures on deleted tasks as normal lifecycle noise in alerting
- Keep a runbook mapping each doorkeeper permanent-failure message to its expected action
When it happens
Trigger: The Asana task that a Phabricator object is mirrored to has been deleted or made inaccessible to every candidate possessed user between import and edge save. The same object later produces new feed stories, each failing permanently.
Common situations: Someone deletes the Asana task manually while Phabricator keeps mirroring updates; the Asana task is moved to a workspace the integration cannot see; de-provisioned users whose tokens can no longer read the task.
Related errors
- No related users have linked Asana accounts.
- Unable to find any Asana user with valid credentials to pull
- Synchronization of parent task from Asana failed!
- Synchronization of child task from Asana failed!
- No Asana provider configured.
AI-assisted analysis of phacility/phabricator@5720a38cfe (2026-08-21).
Data as JSON: /api/errors/3d070ef7d9caa874.
Report an issue: GitHub.