transloadit/uppy · error
s3: filename returned from `getKey` must be a string
Error message
s3: filename returned from `getKey` must be a string
What it means
Companion's S3 multipart upload initiation calls the user-configured getKey(options) function to derive the S3 object key from the request. If getKey returns anything other than a string (undefined, null, number, object), Companion responds with HTTP 500 and this message. It exists to prevent sending an invalid Key to AWS S3's CreateMultipartUpload, which would fail opaquely otherwise.
Source
Thrown at packages/@uppy/companion/src/server/controllers/s3.ts:213
return
}
const truncatedFilename = truncateFilename(
filename,
req.companion.options.maxFilenameLength,
)
const bucket = getBucket({
bucketOrFn: config.bucket,
req,
filename: truncatedFilename,
metadata,
})
const key = config.getKey({ req, filename: truncatedFilename, metadata })
if (typeof key !== 'string') {
res.status(500).json({
error: 's3: filename returned from `getKey` must be a string',
})
return
}
if (typeof type !== 'string') {
res.status(400).json({ error: 's3: content type must be a string' })
return
}
const params = {
Bucket: bucket,
Key: key,
ContentType: type,
Metadata: rfc2047EncodeMetadata(metadata),
...(config.acl != null && { ACL: config.acl }),
...(config.awsSse != null && { ServerSideEncryption: config.awsSse }),
...(config.awsSseKmsKeyId != null && {
SSEKMSKeyId: config.awsSseKmsKeyId,View on GitHub (pinned to 5d4dedd02a)
Solutions
- Fix your getKey function so every code path returns a string, e.g. return \`\${userId}/\${filename}\` with a fallback like filename || 'upload'.
- Log the arguments (req, filename, metadata) inside getKey to see which input is missing and causes an undefined return.
- If getKey is async, make sure Companion's config supports it and that you return (not just resolve) the string value.
- As a last resort remove getKey to use Companion's default key generation.
Example fix
// before
getKey: ({ req, filename }) => {
if (req.user) {
return `${req.user.id}/${filename}`
}
// falls through, returns undefined -> 500
}
// after
getKey: ({ req, filename }) =>
`${req.user?.id ?? 'anonymous'}/${filename}` Defensive patterns
Strategy: type-guard
Validate before calling
const results = []
for (const { req, filename, metadata } of cases) {
const key = config.getKey({ req, filename, metadata })
if (typeof key !== 'string') throw new Error(`getKey returned ${key} for ${filename}`)
} Type guard
const isStringKey = (v: unknown): v is string => typeof v === 'string' && v.length > 0
// wrap your getKey:
const safeGetKey = (opts) => {
const k = getKey(opts)
return isStringKey(k) ? k : `fallback/${opts.filename}`
} Prevention
- Always give getKey a default branch that returns a string
- Unit-test getKey with missing metadata/user to confirm it still returns a string
- Never let getKey implicitly return undefined (add explicit returns on all paths)
When it happens
Trigger: POST to /s3/multipart with a body whose getKey implementation returns a non-string, e.g. an arrow function without a return statement (returns undefined), a function returning a number, or a getKey that conditionally returns undefined for some inputs (e.g. missing metadata field).
Common situations: Writing a custom getKey in companion options that forgets a return path, refactoring getKey to async without awaiting, returning a template that evaluates to undefined because a variable is missing, or copying an example getKey that assumes fields not present in the upload metadata.
Related errors
- The S3 client is not configured on this companion instance.
- s3: bucket key must be a string or a function resolving the
- s3: content type must be a string
- s3: the object key must be passed as a query parameter. For
- s3: uploadId must be provided.
AI-assisted analysis of transloadit/uppy@5d4dedd02a (2026-08-28).
Data as JSON: /api/errors/0003e2cde22c62c6.
Report an issue: GitHub.