{"record":{"id":"dfd9a8ec8388c7fa","repo":"transloadit/uppy","slug":"region-must-be-a-string","errorCode":null,"errorMessage":"region must be a string","messagePattern":"region must be a string","errorType":"validation","errorClass":"TypeError","httpStatus":null,"severity":"warning","filePath":"packages/@uppy/aws-s3/src/index.ts","lineNumber":197,"sourceCode":"  // --------------------------------------------------------------------------\n\n  #initS3Client(): void {\n    if ('companionEndpoint' in this.opts) {\n      if (typeof this.opts.companionEndpoint !== 'string') {\n        throw new TypeError('companionEndpoint must be a string')\n      }\n      this.#s3Client = new S3Companion({\n        companionEndpoint: this.opts.companionEndpoint,\n      })\n    } else if ('getCredentials' in this.opts) {\n      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(","sourceCodeStart":179,"sourceCodeEnd":215,"githubUrl":"https://github.com/transloadit/uppy/blob/5d4dedd02a1ac0ae022c75c54aca76558f88e256/packages/@uppy/aws-s3/src/index.ts#L179-L215","documentation":"The remote file download endpoint /:providerName/get/:id requires a non-empty string id parameter. This 400 is returned when the id is missing, not a string, or empty — i.e. the URL path did not include a usable file identifier.","triggerScenarios":"Calling /get/ with no id, an empty segment, or a malformed route (e.g. the id got stripped by a proxy rewrite). Usually hit when a stored file id is undefined and interpolated into a URL by a client.","commonSituations":"Persisted upload metadata (e.g. Golden Retriever or tus metadata) contains an undefined/null provider file id; a URL template bug in custom frontend code; path rewriting in a reverse proxy dropping the final segment.","solutions":["Inspect the failing request URL and confirm the id segment is present and non-empty","Log the file object before building the URL to confirm file.id is defined","Fix proxy rewrite rules so the full path reaches Companion","Regenerate/re-fetch the file listing so ids are populated"],"exampleFix":"// before\ncompanion.fetchProviderFile(`${url}/get/${file.id}`) // file.id undefined -> /get/undefined or /get/\n// after\nif (!file.id) throw new Error('missing file id')\ncompanion.fetchProviderFile(`${url}/get/${file.id}`)","handlingStrategy":"validation","validationCode":"if (typeof file.id !== 'string' || file.id.length === 0) {\n  throw new Error('cannot build get URL without a file id')\n}","typeGuard":"const hasFileId = (f: unknown): f is { id: string } =>\n  typeof (f as any)?.id === 'string' && (f as any).id.length > 0","tryCatchPattern":null,"preventionTips":["Always validate file.id before constructing provider URLs","Type provider file payloads end-to-end","Test URL builders with undefined inputs"],"tags":["companion","provider-files","http-400","validation"],"backgroundTag":"invalid-route-parameter","analyzedSha":"5d4dedd02a1ac0ae022c75c54aca76558f88e256","analyzedAt":"2026-08-28T12:18:41.267Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}