{"record":{"id":"0726173f8f231432","repo":"transloadit/uppy","slug":"companion-request-failed-response-statustext","errorCode":null,"errorMessage":"Companion request failed: ${response.statusText}","messagePattern":"Companion request failed: (.+?)","errorType":"http","errorClass":"Error","httpStatus":null,"severity":"info","filePath":"packages/@uppy/aws-s3/src/s3-client/CompanionS3.ts","lineNumber":23,"sourceCode":"\n/**\n * S3Companion is an S3Client that interacts with a Companion server to perform S3 operations.\n */\nclass S3Companion extends S3Client {\n  readonly companionEndpoint: string\n\n  constructor({\n    companionEndpoint,\n    requestAbortTimeout,\n  }: { companionEndpoint: string; requestAbortTimeout?: number | undefined }) {\n    super({ requestAbortTimeout })\n    this.companionEndpoint = companionEndpoint\n  }\n\n  private async _fetch(path: string, opts?: RequestInit) {\n    const response = await fetch(`${this.companionEndpoint}/s3${path}`, opts)\n    if (!response.ok) {\n      throw new Error(`Companion request failed: ${response.statusText}`)\n    }\n    return response\n  }\n\n  private async _request({\n    url,\n    method,\n    data,\n    onProgress,\n    signal,\n    contentType,\n  }: {\n    url: string\n    method: IT.HttpMethod\n    data?: XMLHttpRequestBodyInit\n    onProgress?: IT.OnProgressFn\n    signal?: AbortSignal\n    contentType?: string","sourceCodeStart":5,"sourceCodeEnd":41,"githubUrl":"https://github.com/transloadit/uppy/blob/5d4dedd02a1ac0ae022c75c54aca76558f88e256/packages/@uppy/aws-s3/src/s3-client/CompanionS3.ts#L5-L41","documentation":"The logout controller cleans the session and returns 400 when no provider instance is available — meaning Companion cannot perform a server-side token revocation for that provider, though it still clears the local session first (cleanSession()).","triggerScenarios":"Calling /logout for a provider that is not configured on this Companion instance, or when the provider session middleware failed to instantiate the provider (bad/expired token).","commonSituations":"Frontend and backend provider lists out of sync; logging out after the provider config was removed; token already expired so provider was not attached.","solutions":["Align the providers configured on Companion with what the client uses","Ensure the request carries a valid provider session token","If the goal is only local cleanup, treat 400 as acceptable — the session was still cleaned server-side"],"exampleFix":"// before\nconst res = await fetch('/companion/box/logout', { method: 'POST' })\nif (!res.ok) throw new Error('logout failed')\n// after\nconst res = await fetch('/companion/box/logout', { method: 'POST' })\n// 400 still means the local session was cleaned; provider revocation was skipped","handlingStrategy":"fallback","validationCode":"const res = await fetch(`${companionUrl}/${provider}/logout`, { method: 'POST' })\n// 400 still means local session was cleaned; treat as success for local logout","typeGuard":null,"tryCatchPattern":"try {\n  await logout()\n} catch {\n  // best-effort: clear local state regardless of server response\n  clearLocalSession()\n}","preventionTips":["Treat logout as best-effort; always clear client-side tokens","Only expect full revocation when the provider is configured server-side"],"tags":["companion","logout","http-400"],"backgroundTag":"provider-not-configured","analyzedSha":"5d4dedd02a1ac0ae022c75c54aca76558f88e256","analyzedAt":"2026-08-28T12:18:41.267Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}