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
- Check the stage/job name in the badge URL against the current azure-pipelines.yml and update it after renames.
- Verify the referenced stage/job actually runs in the build for the queried branch.
- 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
- Keep badge URLs in sync with stage/job names in azure-pipelines.yml
- Use exact timeline names (display names may differ from YAML ids)
- Verify the stage/job runs on the queried branch
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.