{"record":{"id":"2213efec5a0504ec","repo":"Budibase/budibase","slug":"github-latest-release-url-not-found-2213ef","errorCode":null,"errorMessage":"Github latest release url not found","messagePattern":"Github latest release url not found","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/server/src/sdk/plugins/update.ts","lineNumber":103,"sourceCode":"async function fetchLatestRelease(\n  repo: string,\n  token: string\n): Promise<ReleaseDetails | null> {\n  const url = `${GITHUB_API_ROOT}/${repo}/releases/latest`\n  const headers = headersForRequest(token)\n  const response = await fetch(url, { headers })\n\n  if (response.status === 404) {\n    return null\n  }\n\n  const data = await response.json()\n  const tarballAsset = (data.assets || []).find(\n    (asset: any) => asset?.content_type === \"application/gzip\"\n  )\n  const tarballUrl = tarballAsset?.browser_download_url\n  if (!tarballUrl) {\n    throw new Error(\"Github latest release url not found\")\n  }\n  const version = normaliseVersion(data.tag_name) || normaliseVersion(data.name)\n  if (!version) {\n    throw new Error(\"Github release version not found\")\n  }\n  return {\n    version,\n    tagName: data.tag_name,\n    name: data.name,\n    htmlUrl: data.html_url,\n    publishedAt: data.published_at,\n    tarballUrl,\n    etag: response.headers.get(\"etag\") || undefined,\n    body: data.body,\n  }\n}\n\nfunction buildUpdateInfo(","sourceCodeStart":85,"sourceCodeEnd":121,"githubUrl":"https://github.com/Budibase/budibase/blob/a81a902e9a8fe55b467d106765f6638f12e35c49/packages/server/src/sdk/plugins/update.ts#L85-L121","documentation":"fetchLatestRelease queries the GitHub latest-release API for a plugin's origin repository and looks for a downloadable asset with content type application/gzip (the plugin tarball). If no such asset exists in the release, it throws this error.","triggerScenarios":"Checking a plugin for updates when the latest GitHub release has no .tar.gz/gzip asset, or the asset's content_type field is set to something else (e.g. application/x-tar or application/octet-stream).","commonSituations":"Plugin maintainers published a release with only source archives (auto-generated 'Source code (zip/tar.gz)' have no content_type asset entry) rather than an uploaded tarball; release assets were deleted; private repos returning a release without assets.","solutions":["Ask the plugin maintainer to upload a .tar.gz asset (with gzip content type) to the GitHub release.","Point the plugin origin at a release/tag that contains a proper tarball asset.","Pin the plugin to a known-good version instead of tracking 'latest'.","Check the release's assets via `gh release view <tag> --json assets` to confirm what is published."],"exampleFix":"// before\ngh release create v1.0.0  (no assets uploaded)\n// after\ngh release create v1.0.0 plugin-1.0.0.tar.gz","handlingStrategy":"fallback","validationCode":"const release = await fetch(latestUrl).then(r => r.json())\nconst hasTarball = (release.assets || []).some(a => a.content_type === \"application/gzip\")\nif (!hasTarball) throw new Error(\"No gzip tarball asset on latest release\")","typeGuard":"const hasGzipAsset = (r: { assets?: { content_type?: string }[] }): boolean => (r.assets ?? []).some(a => a?.content_type === \"application/gzip\")","tryCatchPattern":"try {\n  rel = await fetchLatestRelease(origin)\n} catch {\n  rel = await getFallbackPinnedRelease(origin) // last known-good version\n}","preventionTips":["Publish releases with an explicitly uploaded .tar.gz asset","Note that auto-generated 'Source code' archives don't count as gzip assets","Pin plugin origins to known-good tags for production","Check release assets with `gh release view` before publishing"],"tags":["github","plugins","release"],"backgroundTag":"github-release-asset-missing","analyzedSha":"a81a902e9a8fe55b467d106765f6638f12e35c49","analyzedAt":"2026-08-29T01:03:10.972Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}