transloadit/uppy · critical · TypeError

s3: bucket key must be a string or a function resolving the

Error message

s3: bucket key must be a string or a function resolving the bucket string

What it means

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.

Source

Thrown at packages/@uppy/companion/src/server/helpers/utils.ts:280

export const getBucket = ({
  bucketOrFn,
  req,
  metadata,
  filename,
}: {
  bucketOrFn: string | GetBucketFn | undefined
  req: Request
  metadata?: Record<string, unknown>
  filename?: string
}): string => {
  const bucket =
    typeof bucketOrFn === 'function'
      ? bucketOrFn({ req, metadata: metadata ?? {}, filename })
      : bucketOrFn

  if (typeof bucket !== 'string' || bucket === '') {
    // This means a misconfiguration or bug
    throw new TypeError(
      's3: bucket key must be a string or a function resolving the bucket string',
    )
  }
  return bucket
}

export const truncateFilename = (
  filename: string,
  maxFilenameLength?: number,
): string => {
  if (
    maxFilenameLength == null ||
    !Number.isFinite(maxFilenameLength) ||
    maxFilenameLength <= 0
  ) {
    // Historically, passing `undefined` resulted in no truncation (slice(0)).
    return filename
  }

View on GitHub (pinned to 5d4dedd02a)

Solutions

  1. Set providerOptions.s3.bucket to a literal bucket name: bucket: 'my-uploads'
  2. If dynamic, make the function always return a string: bucket: ({ req, metadata }) => req.headers['x-upload-bucket'] ?? 'default-uploads'
  3. Check the env vars / inputs the bucket function reads are actually present at request time
  4. Add a startup assertion that validates the resolved bucket is a non-empty string

Example fix

// before
providerOptions: {
  s3: { bucket: ({ req }) => req.headers['x-bucket'] }, // may be undefined
},

// after
providerOptions: {
  s3: { bucket: ({ req }) => String(req.headers['x-bucket'] ?? 'default-uploads') },
},
Defensive patterns

Strategy: validation

Validate before calling

// Validate bucket config before starting upload flows
const bucket = typeof(s3Opts.bucket) === 'function'
  ? s3Opts.bucket({ req: mockReq, metadata: {}, filename: 'probe.txt' })
  : s3Opts.bucket
if (typeof bucket !== 'string' || bucket === '') throw new TypeError('bad bucket config')

Type guard

function isValidBucket(bucket: unknown): bucket is string {
  return typeof bucket === 'string' && bucket.length > 0
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of transloadit/uppy@5d4dedd02a (2026-08-28). Data as JSON: /api/errors/b1e8017260c58ac8. Report an issue: GitHub.