badges/shields · error · NotFound

${recordType.toLowerCase()} not found

Error message

${recordType.toLowerCase()} not found

What it means

After fetching a build's timeline, getStageOrJobResult searches the timeline records for a record whose type ('Stage' or 'Job') and name match the requested stage/job. If no matching record exists it throws NotFound with '<stage|job> not found'. The build exists, but the named stage or job does not appear in its timeline.

Source

Thrown at services/azure-devops/azure-devops-build.service.js:173

    buildId,
    stage,
    job,
    httpErrors,
  ) {
    const url = `https://dev.azure.com/${organization}/${projectId}/_apis/build/builds/${buildId}/timeline`
    const { records } = await this.fetch({
      url,
      options: {},
      schema: timelineSchema,
      httpErrors,
    })
    const recordType = job ? 'Job' : 'Stage'
    const recordName = job || stage
    const record = records.find(
      r => r.type === recordType && r.name === recordName,
    )
    if (!record) {
      throw new NotFound({
        prettyMessage: `${recordType.toLowerCase()} not found`,
      })
    }
    return record.result
  }

  async handle(
    { organization, projectId, definitionId, branch },
    { stage, job },
  ) {
    const httpErrors = {
      404: 'build pipeline not found',
    }
    // Microsoft documentation: https://docs.microsoft.com/en-us/rest/api/azure/devops/build/builds/list
    const url = `https://dev.azure.com/${organization}/${projectId}/_apis/build/builds`
    const options = {
      searchParams: {
        definitions: definitionId,

View on GitHub (pinned to 766fd8bc89)

Solutions

  1. Check the stage/job name in the badge URL against the current azure-pipelines.yml and update it after renames.
  2. Verify the referenced stage/job actually runs in the build for the queried branch.
  3. Match the name exactly as it appears in the timeline (display name vs. YAML identifier can differ).

Example fix

// before
.../build/myorg/myproj/42?stage=BuildOld
// after
.../build/myorg/myproj/42?stage=Build
Defensive patterns

Strategy: validation

Validate before calling

const timeline = await fetchBuildTimeline(org, project, buildId)
const exists = timeline.records.some(
  r => r.type === 'Stage' && r.name === stageName
)
if (!exists) throw new Error(`Stage '${stageName}' not in build timeline`)

Type guard

function findRecord(records, type, name) {
  return records.find(r => r.type === type && r.name === name) ?? null
}

Try / catch

try {
  return await service.result(params)
} catch (e) {
  if (e instanceof NotFound) return renderBadge('unknown stage/job')
  throw e
}

Prevention

When it happens

Trigger: Requesting an Azure DevOps build badge with a stage or job query parameter whose name does not match any timeline record: records.find(r => r.type === recordType && r.name === recordName) returns undefined, throwing NotFound with 'stage not found' or 'job not found'.

Common situations: Renaming a stage/job in azure-pipelines.yml while old badge URLs still reference the old name; typos in the stage/job name (names are case-sensitive here); querying a job that only exists in some branches or conditional compilations; YAML path separators vs. display names.

Understand the failure class

Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.

Related errors


AI-assisted analysis of badges/shields@766fd8bc89 (2026-08-30). Data as JSON: /api/errors/4bed9c82c921b298. Report an issue: GitHub.