appwrite/appwrite · error · Appwrite\Extend\Exception

user_target_not_found

user_target_not_found

Error message

The target could not be found.

What it means

Thrown by the update-push-target endpoint (PUT/PATCH /v1/account/targets/:targetId) when the target document fetched for the given targetId is empty. The target does not exist. Lookup uses authorization->skip so absence is real, not a permission issue.

Source

Thrown at app/controllers/api/account.php:4733

                model: Response::MODEL_TARGET,
            )
        ],
        contentType: ContentType::JSON
    ))
    ->param('targetId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Target ID.', false, ['dbForProject'])
    ->param('identifier', '', new Text(Database::LENGTH_KEY), 'The target identifier (token, email, phone etc.)')
    ->inject('queueForEvents')
    ->inject('user')
    ->inject('request')
    ->inject('response')
    ->inject('dbForProject')
    ->inject('authorization')
    ->action(function (string $targetId, string $identifier, Event $queueForEvents, Document $user, Request $request, Response $response, Database $dbForProject, Authorization $authorization) {

        $target = $authorization->skip(fn () => $dbForProject->getDocument('targets', $targetId));

        if ($target->isEmpty()) {
            throw new Exception(Exception::USER_TARGET_NOT_FOUND);
        }

        if ($user->getId() !== $target->getAttribute('userId')) {
            throw new Exception(Exception::USER_TARGET_NOT_FOUND);
        }

        if ($identifier) {
            $target
                ->setAttribute('identifier', $identifier)
                ->setAttribute('expired', false);
        }

        $detector = new Detector($request->getUserAgent());
        $detector->skipBotDetection(); // OPTIONAL: If called, bot detection will completely be skipped (bots will be detected as regular devices then)

        $device = $detector->getDevice();

        $target->setAttribute('name', "{$device['deviceBrand']} {$device['deviceModel']}");

View on GitHub (pinned to cd368e707d)

Solutions

  1. Before updating, ensure the target still exists (GET) or refresh the local target list.
  2. On this error, register a new target via createTarget('unique()', ...) and store the returned ID.
  3. Do not cache targetId indefinitely; refetch from the server when the app starts.

Example fix

// before: update a cached target id that may be gone
await account.updateTarget(cachedTargetId, { identifier: newToken });

// after: fall back to create when the target is missing
try {
  await account.updateTarget(cachedTargetId, { identifier: newToken });
} catch (e) {
  if (e.code === 'user_target_not_found') {
    const created = await account.createTarget('unique()', { identifier: newToken, providerId });
    cacheTargetId(created.$id);
  } else { throw e; }
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Verify the cached target ID is still listed before updating.
const targets = await account.listTargets();
const exists = targets.targets.some(t => t.$id === cachedId);
if (!exists) throw new ClientError('Target is gone; re-register.');

Type guard

function isTargetIdLike(id) {
  return typeof id === 'string' && id.length >= 8;
}

Try / catch

try {
  await account.updateTarget(cachedId, { identifier: token });
} catch (e) {
  if (e.code === 'user_target_not_found') {
    const created = await account.createTarget('unique()', { identifier: token, providerId });
    cacheTargetId(created.$id);
  } else { throw e; }
}

Prevention

When it happens

Trigger: PUT /v1/account/targets/:targetId where :targetId does not exist. Common after the target was deleted, the ID is stale, or the client is using an invented/hardcoded value.

Common situations: Target was purged by a delete call but the client cached the ID; push-token refresh runs after the device row was deleted; client constructed the URL with a wrong ID; database migration dropped targets.

Related errors


AI-assisted analysis of appwrite/appwrite@cd368e707d (2026-08-12). Data as JSON: /api/errors/cc98bdb73a79e2ee. Report an issue: GitHub.