transloadit/uppy · error · Error

Missing S3 object key for resuming upload

Error message

Missing S3 object key for resuming upload

What it means

During the OAuth callback, Companion failed to verify the state/origin or exchange the code for tokens, so it responds 400 with an HTML error page (either the modern auth-callback page keyed by authCallbackToken, or a legacy HTML page for old Uppy clients).

Source

Thrown at packages/@uppy/aws-s3/src/S3Uploader.ts:242

      }
      this.#options.s3Client
        .abortMultipartUpload({ key: this.#key, uploadId: this.#uploadId })
        .catch((abortErr) => {
          this.#options.log?.(abortErr, 'warning')
        })
    }

    this.#key = undefined
    this.#uploadId = undefined
    this.#uploadHasStarted = false
  }

  async #resumeMultipartUpload(
    uploadId: string,
    signal: AbortSignal,
  ): Promise<void> {
    if (!this.#key) {
      throw new Error('Missing S3 object key for resuming upload')
    }
    const existingParts = await this.#options.s3Client.listParts({
      uploadId,
      key: this.#key,
      signal,
    })
    // Sync local state with S3 - mark already-uploaded parts
    for (const part of existingParts) {
      const chunkIndex = part.partNumber - 1
      if (chunkIndex >= 0 && chunkIndex < this.#chunkState.length) {
        this.#chunkState[chunkIndex].uploaded = this.#chunks[chunkIndex].size
        this.#chunkState[chunkIndex].etag = part.etag
      }
    }
    // Emit progress update to reflect already-uploaded parts
    this.#onProgress()
    await this.#uploadRemainingParts(signal)
  }

View on GitHub (pinned to 5d4dedd02a)

Solutions

  1. Retry the connect flow from the beginning (get a fresh state and authorization code)
  2. Verify companion options `server.origins` includes the exact frontend origin (scheme, host, port)
  3. Ensure cookies are not blocked and the Companion domain matches between the redirect and the callback
  4. Check server clock and state expiry (authStateExpiry) configuration

Example fix

// before
companion({ server: { origins: ['https://wrong.example.com'] } })
// after
companion({ server: { origins: ['https://app.example.com'] } })
Defensive patterns

Strategy: retry

Validate before calling

// before redirecting to connect, confirm the frontend origin is registered
const allowed = companionOrigins.includes(window.location.origin)
if (!allowed) throw new Error('origin not allowed by Companion')

Try / catch

try {
  await startOAuthFlow()
} catch (e) {
  if (e.status === 400) return restartOAuthFlow() // fresh state
  throw e
}

Prevention

When it happens

Trigger: The state cookie/signature does not match (expired or cross-origin state), the authorization code was reused or expired, grant's token exchange failed, or the origin in the state does not match the requesting origin (origin mismatch protection).

Common situations: User sits on the OAuth consent screen too long and the state expires; cookies blocked by the browser so state cannot be round-tripped; companionOptions.server.oauthDomain or origins misconfigured so the origin check fails; retrying/refreshing an already-consumed callback URL.

Related errors


AI-assisted analysis of transloadit/uppy@5d4dedd02a (2026-08-28). Data as JSON: /api/errors/1db3be8e4f3f1204. Report an issue: GitHub.