{"record":{"id":"0455d7731b437bce","repo":"immich-app/immich","slug":"the-api-key-header-can-only-be-set-using-setapikey","errorCode":null,"errorMessage":"The API key header can only be set using setApiKey().","messagePattern":"The API key header can only be set using setApiKey\\(\\)\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/sdk/src/index.ts","lineNumber":48,"sourceCode":"};\n\nexport const setHeader = (key: string, value: string) => {\n  assertNoApiKey(key);\n  defaults.headers = defaults.headers || {};\n  defaults.headers[key] = value;\n};\n\nexport const setHeaders = (headers: Record<string, string>) => {\n  defaults.headers = defaults.headers || {};\n  for (const [key, value] of Object.entries(headers)) {\n    assertNoApiKey(key);\n    defaults.headers[key] = value;\n  }\n};\n\nconst assertNoApiKey = (headerKey: string) => {\n  if (headerKey.toLowerCase() === 'x-api-key') {\n    throw new Error('The API key header can only be set using setApiKey().');\n  }\n};\n\nexport const jsonOnly =\n  (impl?: typeof fetch): typeof fetch =>\n  async (input, options) => {\n    const response = await (impl ?? fetch)(input, options);\n\n    const expectsJson = new Headers(options?.headers)\n      .get('accept')\n      ?.includes('json');\n    if (!expectsJson || response.status === 204) {\n      return response;\n    }\n\n    const contentType = response.headers.get('content-type');\n    if (!contentType?.includes('json')) {\n      throw new MalformedResponseError(","sourceCodeStart":30,"sourceCodeEnd":66,"githubUrl":"https://github.com/immich-app/immich/blob/e55ac299a4ec7cb372e35dbf2c6c05ee9ce77f6c/packages/sdk/src/index.ts#L30-L66","documentation":"The Immich SDK reserves the 'x-api-key' header for setApiKey() only. setHeader/setHeaders refuse any attempt to set a header named x-api-key (case-insensitive) so the credential can't be duplicated or overridden inconsistently by user code.","triggerScenarios":"Calling sdk.setHeader('x-api-key', ...) or passing { 'x-api-key': ... } (any casing: X-API-Key, etc.) to setHeaders; commonly done by users who treat the SDK like raw fetch and copy auth-header code from curl examples.","commonSituations":"Migrating code from a plain fetch wrapper to the Immich SDK, wiring headers from environment config that already contains the key, or bulk-copying a defaults.headers object that includes x-api-key.","solutions":["Remove the x-api-key header from setHeader/setHeaders calls and call setApiKey(value) instead.","Filter x-api-key out of any header object sourced from config/env before passing to setHeaders.","Rename the env/config field so it feeds setApiKey, not a generic header list."],"exampleFix":"// before\nsdk.setHeaders({ 'x-api-key': process.env.IMMICH_API_KEY! });\n// after\nsdk.setApiKey(process.env.IMMICH_API_KEY!);","handlingStrategy":"validation","validationCode":"const RESERVED = ['x-api-key'];\nconst safeHeaders = Object.fromEntries(\n  Object.entries(userHeaders).filter(([k]) => !RESERVED.includes(k.toLowerCase()))\n);\nsdk.setHeaders(safeHeaders);","typeGuard":null,"tryCatchPattern":"try {\n  sdk.setHeaders(headers);\n} catch (e) {\n  if (String(e).includes('setApiKey')) {\n    sdk.setApiKey((headers as any)['x-api-key']);\n  } else throw e;\n}","preventionTips":["Always supply credentials via setApiKey(), never via generic header maps","Strip reserved auth headers from config/env-derived header objects","When porting raw fetch code, migrate x-api-key usage to setApiKey first"],"tags":["api-key","headers","sdk","authentication"],"backgroundTag":"invalid-argument-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"}