{"record":{"id":"e220f8a341ea19fb","repo":"transloadit/uppy","slug":"signrequest-must-be-a-function","errorCode":null,"errorMessage":"signRequest must be a function","messagePattern":"signRequest must be a function","errorType":"validation","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"packages/@uppy/aws-s3/src/index.ts","lineNumber":208,"sourceCode":"      if (typeof this.opts.s3Endpoint !== 'string') {\n        throw new TypeError('s3Endpoint must be a string')\n      }\n      if (typeof this.opts.getCredentials !== 'function') {\n        throw new TypeError('getCredentials must be a function')\n      }\n      if (this.opts.region != null && typeof this.opts.region !== 'string') {\n        throw new TypeError('region must be a string')\n      }\n\n      // Mode: Temporary credentials (client-side signing)\n      this.#s3Client = new S3mini({\n        endpoint: this.opts.s3Endpoint,\n        getCredentials: this.opts.getCredentials,\n        region: this.opts.region,\n      })\n    } else if ('signRequest' in this.opts) {\n      if (typeof this.opts.signRequest !== 'function') {\n        throw new TypeError('signRequest must be a function')\n      }\n      // Mode: Custom signing function\n      this.#s3Client = new S3mini({\n        signRequest: this.opts.signRequest,\n      })\n    } else {\n      throw new TypeError(\n        'One of options `companionEndpoint`, `signRequest`, or `getCredentials` is required',\n      )\n    }\n  }\n\n  // --------------------------------------------------------------------------\n  // Upload Entry Point\n  // --------------------------------------------------------------------------\n\n  #upload = async (fileIDs: string[]): Promise<void> => {\n    if (fileIDs.length === 0) return","sourceCodeStart":190,"sourceCodeEnd":226,"githubUrl":"https://github.com/transloadit/uppy/blob/5d4dedd02a1ac0ae022c75c54aca76558f88e256/packages/@uppy/aws-s3/src/index.ts#L190-L226","documentation":"The get controller requires an initialized provider instance on req.companion (set by middleware once the user is authenticated). If provider is falsy the request cannot proceed and 400 is returned before attempting size/stream operations.","triggerScenarios":"Requesting /get for a provider that is not enabled/configured on this Companion, or hitting the endpoint without a valid provider session so the middleware never attached the provider instance.","commonSituations":"Frontend configured with a provider the backend does not have key/secret for; expired/invalid uppyAuthToken so provider instantiation was skipped; mismatched provider names between frontend and Companion config.","solutions":["Verify the provider is in companionOptions.providers with valid key/secret","Ensure the client sends a valid, unexpired token for the provider session","Align provider names (case-sensitive) between Uppy's frontend options and Companion","Check middleware logs to see why the provider instance was not created"],"exampleFix":"// before\ncompanion({ providers: ['drive'] }) // client requests box\n// after\ncompanion({ providers: ['drive', 'box'], providerOptions: { box: { key: '...', secret: '...' } } })","handlingStrategy":"validation","validationCode":"if (!enabledProviders.includes(providerName)) {\n  throw new Error(`provider ${providerName} not available`)\n}","typeGuard":"const isProviderAvailable = (p: string): boolean =>\n  enabledProviders.includes(p) && Boolean(providerOptions[p]?.key)","tryCatchPattern":"try {\n  await providerClient.get(file)\n} catch (e) {\n  if (e.status === 400) return reAuthenticate() // session/provider missing\n  throw e\n}","preventionTips":["Check token expiry before provider calls and re-auth proactively","Mirror provider config between client and server","Handle 400 from provider endpoints as a signal to re-run the OAuth flow"],"tags":["companion","provider-files","http-400","provider-session"],"backgroundTag":"provider-not-configured","analyzedSha":"5d4dedd02a1ac0ae022c75c54aca76558f88e256","analyzedAt":"2026-08-28T12:18:41.267Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}