{"record":{"id":"8b1761fc92e999f6","repo":"makeplane/plane","slug":"invalid-file-type-please-select-an-image","errorCode":null,"errorMessage":"Invalid file type. Please select an image.","messagePattern":"Invalid file type\\. Please select an image\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"apps/web/helpers/cover-image.helper.ts","lineNumber":221,"sourceCode":"    entityIdentifier: string;\n    entityType: EFileAssetType;\n    isUserAsset?: boolean;\n  }\n): Promise<string> => {\n  const { workspaceSlug, entityIdentifier, entityType, isUserAsset = false } = uploadConfig;\n\n  // Fetch the local image\n  const response = await fetch(imageUrl);\n\n  if (!response.ok) {\n    throw new Error(`Failed to fetch image: ${response.statusText}`);\n  }\n\n  const blob = await response.blob();\n\n  // Validate it's actually an image\n  if (!blob.type.startsWith(\"image/\")) {\n    throw new Error(\"Invalid file type. Please select an image.\");\n  }\n\n  const fileName = imageUrl.split(\"/\").pop()?.split(\"?\")[0] || \"image.jpg\";\n  const file = new File([blob], fileName, { type: blob.type });\n\n  // Upload based on context\n  if (isUserAsset) {\n    const uploadResult = await fileService.uploadUserAsset(\n      {\n        entity_identifier: entityIdentifier,\n        entity_type: entityType,\n      },\n      file\n    );\n    return uploadResult.asset_url;\n  } else {\n    if (!workspaceSlug) {\n      throw new Error(\"Workspace slug is required for workspace asset upload\");","sourceCodeStart":203,"sourceCodeEnd":239,"githubUrl":"https://github.com/makeplane/plane/blob/1c8a60f858d8472aa56e29994ec1c7926da2c6ce/apps/web/helpers/cover-image.helper.ts#L203-L239","documentation":"Thrown by the cover-image upload helper after a local image URL is fetched. The code reads the response blob and checks that its MIME type starts with 'image/'; anything else (HTML error pages, octet-stream, PDFs) is rejected before being wrapped into a File and uploaded via fileService. The post-fetch check exists because URL extensions and filenames are unreliable signals of real content type.","triggerScenarios":"Calling the cover-image helper with a URL whose response 'Content-Type' is not an image MIME (e.g. text/html for an SPA route or error page, application/octet-stream from a misconfigured CDN, application/pdf). Also when the blob decodes without a recognized type (blob.type === '').","commonSituations":"Passing a frontend route or 404 HTML page instead of the raw asset URL; CDN/proxy stripping Content-Type or serving binary/octet-stream; file still transcoding on the server so the link returns JSON metadata; cross-origin response where the gateway rewrites the type.","solutions":["Open the URL directly in a browser or run `curl -I <url>` and confirm the Content-Type header is image/*.","If the server returns application/octet-stream for real images, fix the upstream/CDN to send the correct image MIME.","Add a HEAD preflight before calling the helper and show a user-facing error if Content-Type is not image/*.","For SVGs, ensure the source emits image/svg+xml; if support is unwanted, block it explicitly upstream."],"exampleFix":"// before\nawait uploadCoverImageHelper(imageUrl, ...);\n\n// after\nconst probe = await fetch(imageUrl, { method: 'HEAD' });\nconst ct = probe.headers.get('content-type') ?? '';\nif (!ct.startsWith('image/')) {\n  throw new Error(`URL is not an image (got ${ct})`);\n}\nawait uploadCoverImageHelper(imageUrl, ...);","handlingStrategy":"validation","validationCode":"async function isImageUrl(url: string): Promise<boolean> {\n  const probe = await fetch(url, { method: 'HEAD' });\n  const ct = probe.headers.get('content-type') ?? '';\n  return probe.ok && ct.startsWith('image/');\n}","typeGuard":"function isImageBlob(blob: Blob): boolean {\n  return typeof blob.type === 'string' && blob.type.startsWith('image/');\n}","tryCatchPattern":"try {\n  await uploadCoverImageHelper(url, ctx);\n} catch (e) {\n  if (e instanceof Error && e.message.startsWith('Invalid file type')) {\n    notifyUser('That URL is not an image. Pick an image file.');\n  } else throw e;\n}","preventionTips":["Pre-validate with a HEAD request","Constrain the file picker to image/* accept attribute","Reject non-image Content-Types at the upload proxy"],"tags":["file-upload","mime-type","cover-image","validation"],"backgroundTag":null,"analyzedSha":"1c8a60f858d8472aa56e29994ec1c7926da2c6ce","analyzedAt":"2026-08-12T14:44:31.636Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}