{"record":{"id":"15ba26b19fca9921","repo":"transloadit/uppy","slug":"missing-s3-object-key-for-aborting-upload","errorCode":null,"errorMessage":"Missing S3 object key for aborting upload","messagePattern":"Missing S3 object key for aborting upload","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"packages/@uppy/aws-s3/src/S3Uploader.ts","lineNumber":223,"sourceCode":"  }\n\n  #pause(): void {\n    this.#abortController?.abort()\n  }\n\n  /**\n   *\n   * @param opts - `abortInS3`: Whether to also abort the multipart upload in S3. Default: true. Set to false to keep the multipart upload in S3 active, allowing for manual cleanup later and preventing accidental data loss if the user later tries to resume the upload.\n   */\n  abort(opts?: { abortInS3?: boolean }): void {\n    this.#abortController?.abort()\n\n    // Clean up event listeners\n    this.#eventManager.remove()\n\n    if (opts?.abortInS3 !== false && this.#uploadId) {\n      if (!this.#key) {\n        throw new Error('Missing S3 object key for aborting upload')\n      }\n      this.#options.s3Client\n        .abortMultipartUpload({ key: this.#key, uploadId: this.#uploadId })\n        .catch((abortErr) => {\n          this.#options.log?.(abortErr, 'warning')\n        })\n    }\n\n    this.#key = undefined\n    this.#uploadId = undefined\n    this.#uploadHasStarted = false\n  }\n\n  async #resumeMultipartUpload(\n    uploadId: string,\n    signal: AbortSignal,\n  ): Promise<void> {\n    if (!this.#key) {","sourceCodeStart":205,"sourceCodeEnd":241,"githubUrl":"https://github.com/transloadit/uppy/blob/5d4dedd02a1ac0ae022c75c54aca76558f88e256/packages/@uppy/aws-s3/src/S3Uploader.ts#L205-L241","documentation":"The OAuth callback route /:providerName/callback requires a providerName URL parameter. This 400 is returned when the parameter is absent, not a string, or an empty string, meaning Express matched the route but the path segment is empty/invalid.","triggerScenarios":"Calling /connect/callback, /connect//callback, or manually constructing a callback URL without the provider segment; also a misconfigured grant callback URL or a provider OAuth app whose redirect URI points at the wrong path.","commonSituations":"Wrong redirect URI configured in the provider's OAuth app; a reverse proxy or load balancer rewriting the path; typo'd companionUrl/server.path options so the callback path is truncated.","solutions":["Ensure the callback URL includes the provider name, e.g. /connect/drive/callback","Check the redirect URI registered with the OAuth provider matches `${companionUrl}/connect/:providerName/callback`","Verify server.path prefix and proxy rewriting are not stripping the provider segment","Confirm the route is mounted via the connect router and not hit directly at root"],"exampleFix":"// before\nGET /connect/callback\n// after\nGET /connect/drive/callback","handlingStrategy":"validation","validationCode":"const providerName = 'drive'\nif (!providerName || typeof providerName !== 'string') throw new Error('provider name required')\nawait fetch(`${companionUrl}/connect/${encodeURIComponent(providerName)}/callback`)","typeGuard":"const isValidProviderName = (p: unknown): p is string =>\n  typeof p === 'string' && p.length > 0","tryCatchPattern":null,"preventionTips":["Build provider URLs from a validated constant list of provider names","Encode provider path segments","Add integration tests asserting callback URLs include the provider segment"],"tags":["companion","oauth","http-400","routing"],"backgroundTag":"invalid-route-parameter","analyzedSha":"5d4dedd02a1ac0ae022c75c54aca76558f88e256","analyzedAt":"2026-08-28T12:18:41.267Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}