{"record":{"id":"18fa41e103d945d7","repo":"immich-app/immich","slug":"invalid-targetsize-targetsize","errorCode":null,"errorMessage":"Invalid targetSize: ' + targetSize","messagePattern":"Invalid targetSize: ' \\+ targetSize","errorType":"exception","errorClass":"Error","httpStatus":500,"severity":"error","filePath":"server/src/controllers/asset-media.controller.ts","lineNumber":156,"sourceCode":"\n    if (viewThumbnailRes instanceof ImmichFileResponse) {\n      await sendFile(res, next, () => Promise.resolve(viewThumbnailRes), this.logger);\n    } else {\n      // viewThumbnailRes is a AssetMediaRedirectResponse\n      // which redirects to the original asset or a specific size to make better use of caching\n      const { targetSize } = viewThumbnailRes;\n      const [reqPath, reqSearch] = req.url.split('?', 2);\n      let redirPath: string;\n      const redirSearchParams = new URLSearchParams(reqSearch);\n      if (targetSize === 'original') {\n        // relative path to this.downloadAsset\n        redirPath = 'original';\n        redirSearchParams.delete('size');\n      } else if (Object.values(AssetMediaSize).includes(targetSize)) {\n        redirPath = reqPath;\n        redirSearchParams.set('size', targetSize);\n      } else {\n        throw new Error('Invalid targetSize: ' + targetSize);\n      }\n      const finalRedirPath = redirPath + '?' + redirSearchParams.toString();\n      return res.redirect(finalRedirPath);\n    }\n  }\n\n  @Get(':id/video/playback')\n  @FileResponse()\n  @Authenticated({ permission: Permission.AssetView, sharedLink: true })\n  @Endpoint({\n    summary: 'Play asset video',\n    description: 'Streams the video file for the specified asset. This endpoint also supports byte range requests.',\n    history: new HistoryBuilder().added('v1').beta('v1').stable('v2'),\n  })\n  async playAssetVideo(\n    @Auth() auth: AuthDto,\n    @Param() { id }: UUIDParamDto,\n    @Res() res: Response,","sourceCodeStart":138,"sourceCodeEnd":174,"githubUrl":"https://github.com/immich-app/immich/blob/e55ac299a4ec7cb372e35dbf2c6c05ee9ce77f6c/server/src/controllers/asset-media.controller.ts#L138-L174","documentation":"Immich's viewAsset controller, when handling image previews, decides between the 'original' path and a sized preview path using a `size` query parameter that must be one of the AssetMediaSize enum values. If the client supplies a size outside the enum, the controller throws this Error, indicating a bad client-side value rather than a missing asset.","triggerScenarios":"Requesting GET /assets/:id/thumbnail (or preview) with ?size=<something> that is not exactly 'preview'/'thumbnail' (the AssetMediaSize members), e.g. size=small, size=full, or a numeric width.","commonSituations":"Hand-written image URLs, cached/third-party clients generating size strings, version drift where a client uses a size name the server no longer supports, or bookmarks/HTML copied with old parameters.","solutions":["Use only the allowed AssetMediaSize values for ?size= (e.g. preview, thumbnail) or omit the parameter entirely for the default.","Update the client/immich-sdk version so URL building matches the server's enum.","URL-encode or avoid interpolating raw user input into the size parameter.","If you need a different resolution, request the original and resize client-side instead of inventing a size name."],"exampleFix":"// before\nGET /api/assets/<id>/thumbnail?size=small   // throws\n// after\nGET /api/assets/<id>/thumbnail?size=preview","handlingStrategy":"validation","validationCode":"const AssetMediaSize = { Preview: 'preview', Thumbnail: 'thumbnail' } as const;\nconst size = new URLSearchParams(q).get('size');\nif (size && !Object.values(AssetMediaSize).includes(size as any)) {\n  throw new Error(`unsupported size '${size}'; use ${Object.values(AssetMediaSize).join('|')}`);\n}","typeGuard":"const isAssetMediaSize = (s: string | null): s is AssetMediaSize =>\n  s !== null && Object.values(AssetMediaSize).includes(s as AssetMediaSize);","tryCatchPattern":"app.get('/assets/:id/thumbnail', async (req, res) => {\n  try {\n    /* request with ?size=... */\n  } catch (e) {\n    if (String(e).startsWith('Invalid targetSize')) {\n      return res.status(400).json({ error: 'size must be preview|thumbnail' });\n    }\n    throw e;\n  }\n});","preventionTips":["Whitelist ?size= against AssetMediaSize before building asset URLs","Never interpolate raw user input into the size parameter","Regenerate clients/URLs after Immich server upgrades in case the enum changed"],"tags":["http","query-parameter","enum","server"],"backgroundTag":"invalid-enum-value","analyzedSha":"e55ac299a4ec7cb372e35dbf2c6c05ee9ce77f6c","analyzedAt":"2026-09-15T07:20:19.675Z","contentChangedAt":"2026-09-15T07:20:19.675Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}