{"record":{"id":"62883ce5b6f1bb7b","repo":"toeverything/AFFiNE","slug":"unsplash-is-not-configured","errorCode":"unsplash_is_not_configured","errorMessage":"Unsplash is not configured.","messagePattern":"Unsplash is not configured\\.","errorType":"exception","errorClass":"UnsplashIsNotConfigured","httpStatus":500,"severity":"error","filePath":"packages/backend/server/src/plugins/copilot/controller.ts","lineNumber":314,"sourceCode":"      );\n\n      return this.mergePingStream(prepared.messageId || '', source$);\n    } catch (err) {\n      metrics.ai.counter('images_stream_errors').add(1, info);\n      return mapSseError(err, info);\n    }\n  }\n\n  @Get('/unsplash/photos')\n  @CallMetric('ai', 'unsplash')\n  async unsplashPhotos(\n    @Req() req: Request,\n    @Res() res: Response,\n    @Query() params: Record<string, string>\n  ) {\n    const { key } = this.config.copilot.unsplash;\n    if (!key) {\n      throw new UnsplashIsNotConfigured();\n    }\n\n    const query = new URLSearchParams(params);\n    const response = await fetch(\n      `https://api.unsplash.com/search/photos?${query}`,\n      {\n        headers: { Authorization: `Client-ID ${key}` },\n        signal: getSignal(req).signal,\n      }\n    );\n\n    res.set({\n      'Content-Type': response.headers.get('Content-Type'),\n      'Content-Length': response.headers.get('Content-Length'),\n      'X-Ratelimit-Limit': response.headers.get('X-Ratelimit-Limit'),\n      'X-Ratelimit-Remaining': response.headers.get('X-Ratelimit-Remaining'),\n    });\n","sourceCodeStart":296,"sourceCodeEnd":332,"githubUrl":"https://github.com/toeverything/AFFiNE/blob/b4c8548c09da21b2898443559a5b846f0ccf5dd8/packages/backend/server/src/plugins/copilot/controller.ts#L296-L332","documentation":"Raised by the copilot controller's GET /unsplash/photos endpoint when the deployment has no Unsplash API key configured. The route proxies Unsplash's photo search for AI features; with copilot.unsplash.key empty (its default), the proxy refuses to forward the request and throws UnsplashIsNotConfigured so the client can show a proper 'not set up' state instead of an upstream 401.","triggerScenarios":"Calling GET /api/copilot/unsplash/photos on any deployment where the copilot.unsplash.key config item was never filled (default key: ''); key set in the wrong environment (staging var set, production empty); config reload not applied after setting the key; key string containing only whitespace.","commonSituations":"Fresh self-hosted installs enabling copilot but skipping the Unsplash integration step; cloud environments where only some regions configure the key; frontends not gating the image-search UI on availability.","solutions":["Set the copilot.unsplash.key config item to a valid Unsplash Access Key (Client-ID) and restart/reload the server.","Verify the key is non-empty in the environment the request actually hits (config dump or diagnostics).","On the client, check the advertised copilot capabilities before showing Unsplash search, and render 'not configured' when absent.","If you do not want the feature, stop calling the endpoint rather than relying on the error."],"exampleFix":"# before\n# .env\nAFFINE_COPILOT_UNSPLASH_KEY=\n\n# after\nAFFINE_COPILOT_UNSPLASH_KEY=your_unsplash_access_key","handlingStrategy":"validation","validationCode":"if (!config.copilot.unsplash.key) {\n  disableUnsplashSearchUi(); // don't call the endpoint unconfigured\n}","typeGuard":"const isUnsplashConfigured = (config: Config): boolean =>\n  Boolean(config.copilot?.unsplash?.key);","tryCatchPattern":"try {\n  const photos = await fetch('/api/copilot/unsplash/photos?query=' + q);\n} catch (e) {\n  if (e?.code === 'unsplash_is_not_configured') {\n    showNotice('Image search is not configured on this server');\n  } else throw e;\n}","preventionTips":["Set copilot.unsplash.key during deployment setup if Unsplash search is wanted.","Expose an integration-availability flag to clients and hide Unsplash UI when unset.","Include the key in environment checklists/diagnostics so empty keys surface at boot."],"tags":["copilot","unsplash","missing-api-key","config","integration"],"backgroundTag":"missing-api-key","analyzedSha":"b4c8548c09da21b2898443559a5b846f0ccf5dd8","analyzedAt":"2026-08-18T21:16:52.546Z","contentChangedAt":"2026-08-18T21:16:52.546Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}