home-assistant/core · warning · UpdateFailed

No data returned from Azure DevOps

Error message

No data returned from Azure DevOps

What it means

Raised as UpdateFailed('No data returned from Azure DevOps') by the ado_exception_none_handler decorator when a wrapped coordinator method returns None instead of raising — the aiohttp call succeeded at the transport level but the DevOps client produced no object (empty/empty-looking response). Separately, aiohttp.ClientError is converted to a message-less UpdateFailed.

Source

Thrown at homeassistant/components/azure_devops/coordinator.py:44

from .data import AzureDevOpsData

BUILDS_QUERY: Final = "?queryOrder=queueTimeDescending&maxBuildsPerDefinition=1"
IGNORED_CATEGORIES: Final[list[Category]] = [Category.COMPLETED, Category.REMOVED]

type AzureDevOpsConfigEntry = ConfigEntry[AzureDevOpsDataUpdateCoordinator]


def ado_exception_none_handler(func: Callable) -> Callable:
    """Handle exceptions or None to always return a value or raise."""

    async def handler(*args, **kwargs):
        try:
            response = await func(*args, **kwargs)
        except aiohttp.ClientError as exception:
            raise UpdateFailed from exception

        if response is None:
            raise UpdateFailed("No data returned from Azure DevOps")

        return response

    return handler


class AzureDevOpsDataUpdateCoordinator(DataUpdateCoordinator[AzureDevOpsData]):
    """Class to manage and fetch Azure DevOps data."""

    client: DevOpsClient
    config_entry: AzureDevOpsConfigEntry
    organization: str
    project: Project

    def __init__(
        self,
        hass: HomeAssistant,
        config_entry: AzureDevOpsConfigEntry,

View on GitHub (pinned to 58a3fdb3ea)

Solutions

  1. Verify the project identifier in the config entry matches an existing project in the Azure DevOps organization.
  2. Re-create the PAT with the correct scopes (Code Read, Build Read, etc.) and re-authenticate.
  3. Confirm the organization name is correct and the account can see the project.
  4. Watch for the sibling case: if UpdateFailed has no message text, the underlying cause was an aiohttp.ClientError — check network/proxy.
Defensive patterns

Strategy: validation

Validate before calling

async def project_exists(client, organization: str, project: str) -> bool:
    projects = await client.get_projects(organization)
    return any(p.name.lower() == project.lower() or p.id == project for p in projects or [])

Type guard

def has_data(response) -> bool:
    return response is not None

Try / catch

from homeassistant.helpers.update_coordinator import UpdateFailed
import aiohttp

try:
    data = await coordinator.async_refresh()
except UpdateFailed as err:
    if not str(err):  # empty message == aiohttp.ClientError underneath
        check_network_proxy()
    else:  # 'No data returned' == empty response: check project/PAT scope
        check_project_and_pat()

Prevention

When it happens

Trigger: get_project()/get_builds()/get_pull_request() etc. return None: project name or ID not found in the organization, wrong PAT scope so the API returns an empty payload, or the organization/project renamed so lookups resolve to nothing.

Common situations: Project was renamed or deleted; PAT lacks the required scope (e.g. Build/Code Read); organization name typo'd; personal access token expired in a way that yields empty responses instead of an auth error.

Related errors


AI-assisted analysis of home-assistant/core@58a3fdb3ea (2026-08-14). Data as JSON: /api/errors/3ef2142297b94275. Report an issue: GitHub.