{"record":{"id":"40cb2a031d4ee38e","repo":"transloadit/uppy","slug":"getcredentials-must-be-a-function-40cb2a","errorCode":null,"errorMessage":"getCredentials must be a function","messagePattern":"getCredentials must be a function","errorType":"validation","errorClass":"TypeError","httpStatus":null,"severity":"critical","filePath":"packages/@uppy/aws-s3/src/s3-client/S3mini.ts","lineNumber":75,"sourceCode":"      const { signRequest } = rest\n      if (!signRequest) {\n        throw new TypeError(\n          'Either signRequest or getCredentials must be provided',\n        )\n      }\n\n      if (signRequest && typeof signRequest !== 'function') {\n        throw new TypeError('signRequest must be a function')\n      }\n\n      this.signRequest = signRequest\n    } else if ('getCredentials' in rest) {\n      const { getCredentials, endpoint } = rest\n      if (typeof endpoint !== 'string' || endpoint.trim().length === 0) {\n        throw new TypeError(C.ERROR_ENDPOINT_REQUIRED)\n      }\n      if (getCredentials && typeof getCredentials !== 'function') {\n        throw new TypeError('getCredentials must be a function')\n      }\n      this.endpoint = new URL(this._ensureValidUrl(endpoint))\n\n      this.getCredentials = getCredentials\n      this.signRequest = this._createCredentialBasedSigner()\n    } else {\n      throw new TypeError(\n        'Either signRequest or getCredentials must be provided',\n      )\n    }\n\n    this.region = region\n    this.requestSizeInBytes = requestSizeInBytes\n  }\n\n  /** Creates a presigner that fetches/caches credentials and generates pre-signed URLs. */\n  private _createCredentialBasedSigner(): IT.SignRequestFn {\n    return async (","sourceCodeStart":57,"sourceCodeEnd":93,"githubUrl":"https://github.com/transloadit/uppy/blob/5d4dedd02a1ac0ae022c75c54aca76558f88e256/packages/@uppy/aws-s3/src/s3-client/S3mini.ts#L57-L93","documentation":"When the getCredentials strategy is used, S3mini validates that getCredentials — if provided — is a function returning {credentials: {accessKeyId, secretAccessKey, sessionToken}, region}. Passing a non-function (string, object, or invoked result) throws this TypeError immediately in the constructor.","triggerScenarios":"new S3mini({ endpoint, getCredentials: '/api/s3/credentials' }) (URL instead of function), or getCredentials: credsObject where the fetched credentials were passed directly rather than a callback that fetches them.","commonSituations":"Passing a static credentials object instead of a refreshable callback (breaks STS session renewal); confusing the endpoint URL with the credential-fetcher; invoking the function during wiring.","solutions":["Pass an async function that fetches and returns {credentials, region}, e.g. hitting your backend's credential endpoint","If you truly have static creds, wrap them: getCredentials: async () => ({ credentials: {...}, region })","Verify the return shape includes accessKeyId/secretAccessKey (and sessionToken for temporary creds)"],"exampleFix":"// before\nnew S3mini({ endpoint, getCredentials: myCredsObject }) // throws\n\n// after\nnew S3mini({\n  endpoint,\n  getCredentials: async () => (await fetch('/api/s3/credentials')).json(),\n})","handlingStrategy":"type-guard","validationCode":"if ('getCredentials' in cfg && cfg.getCredentials != null && typeof cfg.getCredentials !== 'function') throw new TypeError('getCredentials must be a function')","typeGuard":"const isGetCredentialsFn = (f: unknown): f is IT.GetCredentialsFn => typeof f === 'function'","tryCatchPattern":null,"preventionTips":["Pass a callback, not a credentials object, so temporary creds can refresh","Validate the callback's return shape in dev (accessKeyId, secretAccessKey)","Rely on S3Config typing to catch non-function values"],"tags":["aws-s3","configuration","type-validation","credentials"],"backgroundTag":"invalid-constructor-arguments","analyzedSha":"5d4dedd02a1ac0ae022c75c54aca76558f88e256","analyzedAt":"2026-08-28T12:18:41.267Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}