{"record":{"id":"b1e8017260c58ac8","repo":"transloadit/uppy","slug":"s3-bucket-key-must-be-a-string-or-a-function-reso","errorCode":null,"errorMessage":"s3: bucket key must be a string or a function resolving the bucket string","messagePattern":"s3: bucket key must be a string or a function resolving the bucket string","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"critical","filePath":"packages/@uppy/companion/src/server/helpers/utils.ts","lineNumber":280,"sourceCode":"export const getBucket = ({\n  bucketOrFn,\n  req,\n  metadata,\n  filename,\n}: {\n  bucketOrFn: string | GetBucketFn | undefined\n  req: Request\n  metadata?: Record<string, unknown>\n  filename?: string\n}): string => {\n  const bucket =\n    typeof bucketOrFn === 'function'\n      ? bucketOrFn({ req, metadata: metadata ?? {}, filename })\n      : bucketOrFn\n\n  if (typeof bucket !== 'string' || bucket === '') {\n    // This means a misconfiguration or bug\n    throw new TypeError(\n      's3: bucket key must be a string or a function resolving the bucket string',\n    )\n  }\n  return bucket\n}\n\nexport const truncateFilename = (\n  filename: string,\n  maxFilenameLength?: number,\n): string => {\n  if (\n    maxFilenameLength == null ||\n    !Number.isFinite(maxFilenameLength) ||\n    maxFilenameLength <= 0\n  ) {\n    // Historically, passing `undefined` resulted in no truncation (slice(0)).\n    return filename\n  }","sourceCodeStart":262,"sourceCodeEnd":298,"githubUrl":"https://github.com/transloadit/uppy/blob/5d4dedd02a1ac0ae022c75c54aca76558f88e256/packages/@uppy/companion/src/server/helpers/utils.ts#L262-L298","documentation":"For S3 uploads, Companion resolves the target bucket from options.s3.bucket, which may be a static string or a function of ({req, metadata, filename}). After resolution, if the result is not a non-empty string, Companion throws this TypeError because it has no valid bucket to upload to — the code itself flags it as a misconfiguration or bug.","triggerScenarios":"Configuring companionOptions.providerOptions.s3.bucket as undefined/null/a number, or a bucket function that returns undefined (e.g. reading from req.headers or metadata that is missing), or returns an empty string when resolving dynamically per-request.","commonSituations":"Missing/typo'd S3 env var used inside a bucket function (e.g. process.env.S3_BUCKET unset so it returns undefined), a bucket resolver that returns void on an unhandled code path, or copy-pasting config from an example that omits bucket.","solutions":["Set providerOptions.s3.bucket to a literal bucket name: bucket: 'my-uploads'","If dynamic, make the function always return a string: bucket: ({ req, metadata }) => req.headers['x-upload-bucket'] ?? 'default-uploads'","Check the env vars / inputs the bucket function reads are actually present at request time","Add a startup assertion that validates the resolved bucket is a non-empty string"],"exampleFix":"// before\nproviderOptions: {\n  s3: { bucket: ({ req }) => req.headers['x-bucket'] }, // may be undefined\n},\n\n// after\nproviderOptions: {\n  s3: { bucket: ({ req }) => String(req.headers['x-bucket'] ?? 'default-uploads') },\n},","handlingStrategy":"validation","validationCode":"// Validate bucket config before starting upload flows\nconst bucket = typeof(s3Opts.bucket) === 'function'\n  ? s3Opts.bucket({ req: mockReq, metadata: {}, filename: 'probe.txt' })\n  : s3Opts.bucket\nif (typeof bucket !== 'string' || bucket === '') throw new TypeError('bad bucket config')","typeGuard":"function isValidBucket(bucket: unknown): bucket is string {\n  return typeof bucket === 'string' && bucket.length > 0\n}","tryCatchPattern":null,"preventionTips":["Always default dynamic bucket functions to a known bucket string","Add a config validation step at Companion startup for providerOptions.s3","Use TypeScript satisfies on your options object to catch wrong types at compile time"],"tags":["companion","s3","configuration","aws"],"backgroundTag":"missing-configuration-value","analyzedSha":"5d4dedd02a1ac0ae022c75c54aca76558f88e256","analyzedAt":"2026-08-28T12:18:41.267Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}