{"record":{"id":"563439c21d3d3c49","repo":"oven-sh/bun","slug":"failed-to-create-gallery-image-definition-defre","errorCode":null,"errorMessage":"Failed to create gallery image definition: ${defResponse.status} ${await defResponse.text()}","messagePattern":"Failed to create gallery image definition: (.+?) (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"scripts/machine.mjs","lineNumber":1205,"sourceCode":"    method: \"PUT\",\n    headers: { \"Authorization\": `Bearer ${token}`, \"Content-Type\": \"application/json\" },\n    body: JSON.stringify({\n      location: location,\n      properties: {\n        osType: \"Windows\",\n        osState: \"Generalized\",\n        hyperVGeneration: \"V2\",\n        architecture: galleryArch,\n        identifier: { publisher: \"bun\", offer: `${os}-${arch}-ci`, sku: imageDefName },\n        features: [\n          { name: \"DiskControllerTypes\", value: \"SCSI, NVMe\" },\n          { name: \"SecurityType\", value: \"TrustedLaunch\" },\n        ],\n      },\n    }),\n  });\n  if (!defResponse.ok && defResponse.status !== 409) {\n    throw new Error(`Failed to create gallery image definition: ${defResponse.status} ${await defResponse.text()}`);\n  }\n\n  // Packer's azure-arm shared_image_gallery_destination always writes\n  // image_version 1.0.0 and 409s if it already exists, so a re-run of\n  // [publish images] would fail on every Windows variant that already\n  // succeeded. Match the AWS path's deregister-then-recreate.\n  // CAUTION: unlike the AWS path (which only deregisters after the new\n  // create-image collides), this deletes the live version BEFORE Packer\n  // has produced a replacement. If this job is canceled or dies mid-bake,\n  // CI is left with no Windows image until a publish run completes.\n  const versionPath = `${galleryPath}/versions/1.0.0`;\n  const existing = await fetch(`https://management.azure.com${versionPath}?api-version=2024-03-03`, {\n    headers: { Authorization: `Bearer ${token}` },\n  });\n  if (existing.ok) {\n    console.log(`[packer] Deleting existing gallery image version 1.0.0 of ${imageDefName} before re-publish`);\n    const del = await fetch(`https://management.azure.com${versionPath}?api-version=2024-03-03`, {\n      method: \"DELETE\",","sourceCodeStart":1187,"sourceCodeEnd":1223,"githubUrl":"https://github.com/oven-sh/bun/blob/8c5296ac459e8252d3cd702f3fbcbb0c249d95d5/scripts/machine.mjs#L1187-L1223","documentation":"While publishing a Windows image, the script PUTs an image-definition create to the Azure Shared Image Gallery REST API and tolerates only 200 and 409 (already exists). Any other non-OK status throws with the status code and response body — auth/RBAC problems, a missing gallery (404), or an invalid payload.","triggerScenarios":"The token's principal lacks Contributor on the resource group/gallery (403); AZURE_GALLERY_NAME/AZURE_RESOURCE_GROUP secrets point at a nonexistent gallery (404); expired token (401); identifier/publisher fields rejected by validation (400).","commonSituations":"New CI service principal without gallery RBAC; wrong gallery name secret after migrating galleries; token fetched for the wrong tenant/subscription.","solutions":["Read the status and body embedded in the error message — they name the exact ARM failure","403/401: verify the principal has Contributor on the gallery's resource group and the token tenant","404: verify AZURE_GALLERY_NAME and AZURE_RESOURCE_GROUP secrets against the real gallery","Re-run publish once permissions/secrets are corrected"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// Cheap pre-flight: the gallery must exist and the token must work\nconst gallery = await fetch(`https://management.azure.com${galleryPath}?api-version=2024-03-03`, {\n  headers: { Authorization: `Bearer ${token}` },\n});\nif (gallery.status === 404) throw new Error('gallery/resource group not found — check AZURE_GALLERY_NAME secrets');\nif (gallery.status === 401 || gallery.status === 403) throw new Error('token/RBAC problem on the gallery');","typeGuard":null,"tryCatchPattern":"try {\n  await createImageDefinition(/* ... */);\n} catch (error) {\n  // 409 is already handled upstream; anything else carries status+body — rethrow verbatim\n  throw error;\n}","preventionTips":["Grant the CI principal Contributor on the gallery's resource group","Keep AZURE_GALLERY_NAME/AZURE_RESOURCE_GROUP secrets in sync with the real Azure state","Log the response body on every non-OK ARM call — statuses alone rarely identify the cause"],"tags":["azure","gallery","permissions","ci","http"],"backgroundTag":null,"analyzedSha":"8c5296ac459e8252d3cd702f3fbcbb0c249d95d5","analyzedAt":"2026-08-16T08:01:58.794Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}