{"record":{"id":"441ef8bbabf7e94e","repo":"transloadit/uppy","slug":"headers-must-be-an-object","errorCode":null,"errorMessage":"headers must be an object","messagePattern":"headers must be an object","errorType":"validation","errorClass":"ValidationError","httpStatus":null,"severity":"error","filePath":"packages/@uppy/companion/src/server/Uploader.ts","lineNumber":137,"sourceCode":"  }\n\n  if (exceedsMaxFileSize(options.companionOptions.maxFileSize, options.size)) {\n    throw new ValidationError('maxFileSize exceeded')\n  }\n\n  // validate fieldname (optional)\n  if (options.fieldname != null && typeof options.fieldname !== 'string') {\n    throw new ValidationError('fieldname must be a string')\n  }\n\n  // validate metadata (optional)\n  if (options.metadata != null && typeof options.metadata !== 'object') {\n    throw new ValidationError('metadata must be an object')\n  }\n\n  // validate headers (optional)\n  if (options.headers != null && typeof options.headers !== 'object') {\n    throw new ValidationError('headers must be an object')\n  }\n\n  // validate protocol (optional)\n  if (\n    options.protocol &&\n    !Object.values(PROTOCOLS).includes(options.protocol)\n  ) {\n    throw new ValidationError('unsupported protocol specified')\n  }\n\n  // s3 uploads don't require upload destination\n  // validation, because the destination is determined\n  // by the server's s3 config\n  if (options.protocol !== PROTOCOLS.s3Multipart) {\n    if (!options.endpoint && !options.uploadUrl) {\n      throw new ValidationError('no destination specified')\n    }\n","sourceCodeStart":119,"sourceCodeEnd":155,"githubUrl":"https://github.com/transloadit/uppy/blob/5d4dedd02a1ac0ae022c75c54aca76558f88e256/packages/@uppy/companion/src/server/Uploader.ts#L119-L155","documentation":"The optional headers option must be an object when provided; Companion will send these headers with the upload request. Non-object values throw a ValidationError.","triggerScenarios":"new Uploader({ headers: 'Authorization: ...' }) or headers: ['x-a'] — any non-object headers value.","commonSituations":"Passing serialized header strings from clients; wrapping headers in an extra layer ({ headers: { headers: {...} } }).","solutions":["Pass headers as an object: headers: { 'x-custom': 'value' }","Parse header strings/arrays into an object first","Remove double-nesting when forwarding options"],"exampleFix":"// before\nheaders: JSON.stringify({ 'x-custom': 'value' })\n\n// after\nheaders: { 'x-custom': 'value' }","handlingStrategy":"type-guard","validationCode":"if (typeof headers === 'string') headers = JSON.parse(headers)","typeGuard":"const isHeadersObject = (h: unknown): h is Record<string, string> => h == null || (typeof h === 'object' && !Array.isArray(h))","tryCatchPattern":"null","preventionTips":["Send headers as objects, not strings","Avoid double-nesting forwarded options"],"tags":["companion","uploader","validation","http-headers"],"backgroundTag":"invalid-option-type","analyzedSha":"5d4dedd02a1ac0ae022c75c54aca76558f88e256","analyzedAt":"2026-08-28T12:18:41.267Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}