{"record":{"id":"2ef9f36bbd778603","repo":"discordjs/discord.js","slug":"invalid-extension-provided-extension-must-be-o","errorCode":null,"errorMessage":"Invalid extension provided: ${extension}\nMust be one of: ${allowedExtensions.join(', ')}","messagePattern":"Invalid extension provided: (.+?)\nMust be one of: (.+?)","errorType":"exception","errorClass":"RangeError","httpStatus":null,"severity":"error","filePath":"packages/rest/src/lib/CDN.ts","lineNumber":389,"sourceCode":"\t *\n\t * @param route - The base cdn route\n\t * @param options - The extension/size options for the link\n\t */\n\tprivate makeURL(\n\t\troute: string,\n\t\t{\n\t\t\tallowedExtensions = ALLOWED_EXTENSIONS,\n\t\t\tbase = this.cdn,\n\t\t\textension = 'webp',\n\t\t\tsize,\n\t\t\tanimated,\n\t\t}: Readonly<MakeURLOptions> = {},\n\t): string {\n\t\t// eslint-disable-next-line no-param-reassign\n\t\textension = String(extension).toLowerCase();\n\n\t\tif (!allowedExtensions.includes(extension)) {\n\t\t\tthrow new RangeError(`Invalid extension provided: ${extension}\\nMust be one of: ${allowedExtensions.join(', ')}`);\n\t\t}\n\n\t\tif (size && !ALLOWED_SIZES.includes(size)) {\n\t\t\tthrow new RangeError(`Invalid size provided: ${size}\\nMust be one of: ${ALLOWED_SIZES.join(', ')}`);\n\t\t}\n\n\t\tconst url = new URL(`${base}${route}.${extension}`);\n\n\t\tif (animated !== undefined) {\n\t\t\turl.searchParams.set('animated', String(animated));\n\t\t}\n\n\t\tif (size) {\n\t\t\turl.searchParams.set('size', String(size));\n\t\t}\n\n\t\treturn url.toString();\n\t}","sourceCodeStart":371,"sourceCodeEnd":407,"githubUrl":"https://github.com/discordjs/discord.js/blob/a81ed8a306d37fdc746e26a634b6a42164ba2c8c/packages/rest/src/lib/CDN.ts#L371-L407","documentation":"makeURL() in CDN.ts builds Discord CDN asset URLs and validates the requested image extension against the list of extensions allowed for that asset type (e.g. ALLOWED_EXTENSIONS for most images, ALLOWED_STICKER_EXTENSIONS for stickers). Before constructing the URL it lowercases the extension and throws this RangeError if it isn't in the allowed list. It exists to fail fast on invalid input rather than produce broken CDN links.","triggerScenarios":"Calling any CDN URL builder (appAsset, appIcon, avatarDecoration, channelIcon, defaultAvatar, discoverySplash, avatar, banner, emoji, icon, etc.) with an `extension` option that is not one of the allowed values ('webp', 'png', 'jpg', 'jpeg', 'gif' — or sticker-specific ones), including misspellings, uppercase strings that aren't normalized by the caller's type (e.g. 'PNG' actually works since it lowercases, but 'bmp' or 'svg' never work), or non-string values coerced via String().","commonSituations":"Hardcoding an extension like 'svg' or 'jpg' when only webp/png/gif are allowed; copying code that used a sticker URL builder's extension for a regular image or vice versa; building extensions dynamically from user input or file paths; upgrading discord.js where allowed extension sets changed.","solutions":["Change the `extension` option to one of the allowed values listed in the error message (usually 'webp', 'png', 'jpg', 'jpeg', or 'gif').","Import ALLOWED_EXTENSIONS (or ALLOWED_STICKER_EXTENSIONS for sticker URLs) from @discordjs/rest to validate or enumerate valid values at runtime.","If the extension comes from user input or a file path, normalize it (lowercase, strip a leading dot) and fall back to 'webp' (the default) when it isn't in the allowed set.","Omit the `extension` option entirely to get the default 'webp'."],"exampleFix":"// before\nconst url = user.displayAvatarURL({ extension: 'svg' });\n// RangeError: Invalid extension provided: svg\n// after\nconst ext = ['webp', 'png', 'jpg', 'jpeg', 'gif'].includes(userInput) ? userInput : 'webp';\nconst url = user.displayAvatarURL({ extension: ext });","handlingStrategy":"validation","validationCode":"import { ALLOWED_EXTENSIONS } from '@discordjs/rest';\nfunction assertValidExtension(ext: string) {\n  const normalized = ext.toLowerCase().replace(/^\\./, '');\n  if (!ALLOWED_EXTENSIONS.includes(normalized as never)) {\n    throw new RangeError(`Unsupported extension: ${normalized}`);\n  }\n  return normalized;\n}","typeGuard":"const isImageExtension = (v: string): v is ImageExtension =>\n  (['webp', 'png', 'jpg', 'jpeg', 'gif'] as const).includes(v as ImageExtension);","tryCatchPattern":"try {\n  url = user.displayAvatarURL({ extension: ext });\n} catch (error) {\n  if (error instanceof RangeError && error.message.startsWith('Invalid extension')) {\n    url = user.displayAvatarURL(); // default webp\n  } else {\n    throw error;\n  }\n}","preventionTips":["Use the ImageExtension type so invalid extensions are caught at compile time.","Never build extensions from raw file paths or unvalidated user input.","Rely on the default 'webp' unless a specific format is required (e.g. 'gif' for animated avatars).","Keep a whitelist map of permitted formats in your app and check before calling URL builders."],"tags":["validation","cdn","input-error","range-error"],"backgroundTag":"invalid-enum-value","analyzedSha":"a81ed8a306d37fdc746e26a634b6a42164ba2c8c","analyzedAt":"2026-08-30T04:07:22.193Z","schemaVersion":2},"datasetVersion":"2026-08-30T08:17:16.595Z"}