{"record":{"id":"d8e2be54c956d105","repo":"transloadit/uppy","slug":"one-of-options-companionendpoint-signrequest","errorCode":null,"errorMessage":"One of options `companionEndpoint`, `signRequest`, or `getCredentials` is required","messagePattern":"One of options `companionEndpoint`, `signRequest`, or `getCredentials` is required","errorType":"validation","errorClass":"TypeError","httpStatus":null,"severity":"warning","filePath":"packages/@uppy/aws-s3/src/index.ts","lineNumber":215,"sourceCode":"        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\n\n    const files = this.uppy.getFilesByIds(fileIDs)\n    const filesToUpload = filterFilesToUpload(files)\n    const filesToEmit = filterFilesToEmitUploadStarted(filesToUpload)\n\n    this.uppy.emit('upload-start', filesToEmit)\n","sourceCodeStart":197,"sourceCodeEnd":233,"githubUrl":"https://github.com/transloadit/uppy/blob/5d4dedd02a1ac0ae022c75c54aca76558f88e256/packages/@uppy/aws-s3/src/index.ts#L197-L233","documentation":"The list controller (directory listing) rejects the request with 400 when no provider instance exists, or when an id parameter is present but is not a string (e.g. an array from a repeated query/path param).","triggerScenarios":"Calling /list with ?id[]=a&id[]=b producing a non-string id; or listing for a provider that is not configured/enabled on the Companion server so provider is falsy.","commonSituations":"Custom frontend code passing array params; provider name mismatch between client and server; missing provider credentials so the instance was never created.","solutions":["Ensure the id (when present) is passed as a single string value","Confirm the provider is enabled with key/secret in companionOptions","Send a valid provider session token with the request"],"exampleFix":"// before\nfetch(`/companion/box/list/?id[]=root&id[]=other`)\n// after\nfetch(`/companion/box/list/?id=root`)","handlingStrategy":"validation","validationCode":"const qs = id ? `?id=${encodeURIComponent(id)}` : ''\nif (Array.isArray(id)) throw new Error('id must be a single string')\nawait fetch(`${companionUrl}/${provider}/list${qs}`)","typeGuard":"const isStringOrUndefined = (v: unknown): v is string | undefined =>\n  v === undefined || typeof v === 'string'","tryCatchPattern":null,"preventionTips":["Never send duplicated query params for id","Validate provider is enabled before listing","Encode path/query params explicitly"],"tags":["companion","provider-files","http-400","query-params"],"backgroundTag":"invalid-request-parameters","analyzedSha":"5d4dedd02a1ac0ae022c75c54aca76558f88e256","analyzedAt":"2026-08-28T12:18:41.267Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}