Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Axios Unable to Handle "204 No Content" Responses #6327

Open
yuchen001 opened this issue Mar 27, 2024 · 7 comments
Open

Axios Unable to Handle "204 No Content" Responses #6327

yuchen001 opened this issue Mar 27, 2024 · 7 comments

Comments

@yuchen001
Copy link

Describe the bug

As of today, Axios seems to be unable to handle requests that result in a "204 No Content" response. Any API request that responds with a 204 status code is treated as a timeout.

Preliminary testing and investigation suggest that the issue might be related to Axios instances.

It might be challenging to provide a publicly accessible test case since finding APIs that reliably respond with a 204 status code could be difficult.

To Reproduce

As of the latest observation, Axios encounters an issue where responses with an empty body (e.g., "204 No Content") are incorrectly treated as timeouts when using an Axios instance.

Notably, when requests are made without creating an Axios instance directly, the behavior is as expected.

Steps to Reproduce:

  • Create an Axios instance.
  • Make a request using the Axios instance to an API endpoint that returns a response with an empty body (e.g., 204 status code).
  • Observe that the Axios instance incorrectly handles the response as a timeout.

Code snippet

No response

Expected behavior

Axios should accurately handle responses with an empty body, such as 204 status codes, without treating them as timeouts.

Axios Version

No response

Adapter Version

No response

Browser

No response

Browser Version

No response

Node.js Version

No response

OS

No response

Additional Library Versions

No response

Additional context/Screenshots

No response

@DigitalBrainJS
Copy link
Collaborator

It might be challenging to provide a publicly accessible test case since finding APIs that reliably respond with a 204 status code could be difficult.

I can't reproduce

const res = await axios.get('https://httpbin.org/status/204');

console.log(res.status, res.statusText); // 204 NO CONTENT

@ttomasini
Copy link

ttomasini commented Apr 29, 2024

I've the same / a similar issue. Unfortunately I cannot disclose the URL but I can contribute some more details:

Axios Version

0.28.0 / 0.28.1

Node Version

was able to reproduce it with v18.20.2

package.json

{
  "name": "axios-test",
  "version": "1.0.0",
  "description": "",
  "main": "app.js",
  "type": "module",
  "dependencies": {
    "axios": "~0.28.1"
  }
}

Code Snippet (minimal example)

#!/usr/bin/env node

import axios from 'axios';

axios.get('https://foo.bar/health/')
  .then(function (response) {
    console.log(response);
  })
  .catch(function (error) {
    console.log(error);
  })
  .finally(function () {});

Log

Expand code...
❯ node app.js
AxiosError: unexpected end of file
    at AxiosError.from (/Users/tio/devel/private/axios-test/node_modules/axios/lib/core/AxiosError.js:86:14)
    at Unzip.handleStreamError (/Users/tio/devel/private/axios-test/node_modules/axios/lib/adapters/http.js:366:29)
    at Unzip.emit (node:events:517:28)
    at emitErrorNT (node:internal/streams/destroy:151:8)
    at emitErrorCloseNT (node:internal/streams/destroy:116:3)
    at process.processTicksAndRejections (node:internal/process/task_queues:82:21) {
  code: 'Z_BUF_ERROR',
  errno: -5,
  config: {
    transitional: {
      silentJSONParsing: true,
      forcedJSONParsing: true,
      clarifyTimeoutError: false
    },
    adapter: [Function: httpAdapter],
    transformRequest: [ [Function: transformRequest] ],
    transformResponse: [ [Function: transformResponse] ],
    timeout: 0,
    xsrfCookieName: 'XSRF-TOKEN',
    xsrfHeaderName: 'X-XSRF-TOKEN',
    maxContentLength: -1,
    maxBodyLength: -1,
    env: { FormData: [Function], Blob: [class Blob] },
    validateStatus: [Function: validateStatus],
    headers: {
      Accept: 'application/json, text/plain, */*',
      'Accept-Encoding': 'gzip,deflate,compress',
      'User-Agent': 'axios/0.28.1'
    },
    method: 'get',
    url: 'https://foo.bar/health/',
    data: undefined
  },
  request: <ref *1> ClientRequest {
    _events: [Object: null prototype] {
      abort: [Function (anonymous)],
      aborted: [Function (anonymous)],
      connect: [Function (anonymous)],
      error: [Function (anonymous)],
      socket: [Function (anonymous)],
      timeout: [Function (anonymous)],
      finish: [Function: requestOnFinish]
    },
    _eventsCount: 7,
    _maxListeners: undefined,
    outputData: [],
    outputSize: 0,
    writable: true,
    destroyed: true,
    _last: true,
    chunkedEncoding: false,
    shouldKeepAlive: false,
    maxRequestsOnConnectionReached: false,
    _defaultKeepAlive: true,
    useChunkedEncodingByDefault: false,
    sendDate: false,
    _removedConnection: false,
    _removedContLen: false,
    _removedTE: false,
    strictContentLength: false,
    _contentLength: 0,
    _hasBody: true,
    _trailer: '',
    finished: true,
    _headerSent: true,
    _closed: true,
    socket: TLSSocket {
      _tlsOptions: [Object],
      _secureEstablished: true,
      _securePending: false,
      _newSessionPending: false,
      _controlReleased: true,
      secureConnecting: false,
      _SNICallback: null,
      servername: 'foo.bar',
      alpnProtocol: false,
      authorized: true,
      authorizationError: null,
      encrypted: true,
      _events: [Object: null prototype],
      _eventsCount: 9,
      connecting: false,
      _hadError: false,
      _parent: null,
      _host: 'foo.bar',
      _closeAfterHandlingError: false,
      _readableState: [ReadableState],
      _maxListeners: undefined,
      _writableState: [WritableState],
      allowHalfOpen: false,
      _sockname: null,
      _pendingData: null,
      _pendingEncoding: '',
      server: undefined,
      _server: null,
      ssl: null,
      _requestCert: true,
      _rejectUnauthorized: true,
      parser: null,
      _httpMessage: [Circular *1],
      write: [Function: writeAfterFIN],
      [Symbol(alpncallback)]: null,
      [Symbol(res)]: null,
      [Symbol(verified)]: true,
      [Symbol(pendingSession)]: null,
      [Symbol(async_id_symbol)]: 10,
      [Symbol(kHandle)]: null,
      [Symbol(lastWriteQueueSize)]: 0,
      [Symbol(timeout)]: null,
      [Symbol(kBuffer)]: null,
      [Symbol(kBufferCb)]: null,
      [Symbol(kBufferGen)]: null,
      [Symbol(kCapture)]: false,
      [Symbol(kSetNoDelay)]: false,
      [Symbol(kSetKeepAlive)]: true,
      [Symbol(kSetKeepAliveInitialDelay)]: 60,
      [Symbol(kBytesRead)]: 392,
      [Symbol(kBytesWritten)]: 180,
      [Symbol(connect-options)]: [Object]
    },
    _header: 'GET /-/health/ready/ HTTP/1.1\r\n' +
      'Accept: application/json, text/plain, */*\r\n' +
      'Accept-Encoding: gzip,deflate,compress\r\n' +
      'User-Agent: axios/0.28.1\r\n' +
      'Host: foo.bar\r\n' +
      'Connection: close\r\n' +
      '\r\n',
    _keepAliveTimeout: 0,
    _onPendingData: [Function: nop],
    agent: Agent {
      _events: [Object: null prototype],
      _eventsCount: 2,
      _maxListeners: undefined,
      defaultPort: 443,
      protocol: 'https:',
      options: [Object: null prototype],
      requests: [Object: null prototype] {},
      sockets: [Object: null prototype] {},
      freeSockets: [Object: null prototype] {},
      keepAliveMsecs: 1000,
      keepAlive: false,
      maxSockets: Infinity,
      maxFreeSockets: 256,
      scheduling: 'lifo',
      maxTotalSockets: Infinity,
      totalSocketCount: 0,
      maxCachedSessions: 100,
      _sessionCache: [Object],
      [Symbol(kCapture)]: false
    },
    socketPath: undefined,
    method: 'GET',
    maxHeaderSize: undefined,
    insecureHTTPParser: undefined,
    joinDuplicateHeaders: undefined,
    path: '/-/health/ready/',
    _ended: true,
    res: IncomingMessage {
      _readableState: [ReadableState],
      _events: [Object: null prototype],
      _eventsCount: 1,
      _maxListeners: undefined,
      socket: [TLSSocket],
      httpVersionMajor: 1,
      httpVersionMinor: 1,
      httpVersion: '1.1',
      complete: true,
#!/usr/bin/env node
      rawHeaders: [Array],
      rawTrailers: [],
      joinDuplicateHeaders: undefined,
      aborted: false,
      upgrade: false,
      url: '',
      method: null,
      statusCode: 204,
      statusMessage: 'No Content',
      client: [TLSSocket],
      _consuming: false,
      _dumped: false,
      req: [Circular *1],
      responseUrl: 'https://foo.bar/health/',
      redirects: [],
      [Symbol(kCapture)]: false,
      [Symbol(kHeaders)]: [Object],
      [Symbol(kHeadersCount)]: 26,
      [Symbol(kTrailers)]: null,
      [Symbol(kTrailersCount)]: 0
    },
    aborted: false,
    timeoutCb: null,
    upgradeOrConnect: false,
    parser: null,
    maxHeadersCount: null,
    reusedSocket: false,
    host: 'foo.bar',
    protocol: 'https:',
    _redirectable: Writable {
      _writableState: [WritableState],
      _events: [Object: null prototype],
      _eventsCount: 3,
      _maxListeners: undefined,
      _options: [Object],
      _ended: true,
      _ending: true,
      _redirectCount: 0,
      _redirects: [],
      _requestBodyLength: 0,
      _requestBodyBuffers: [],
      _onNativeResponse: [Function (anonymous)],
      _currentRequest: [Circular *1],
      _currentUrl: 'https://foo.bar/health/',
      [Symbol(kCapture)]: false
    },
    [Symbol(kCapture)]: false,
    [Symbol(kBytesWritten)]: 0,
    [Symbol(kNeedDrain)]: false,
    [Symbol(corked)]: 0,
    [Symbol(kOutHeaders)]: [Object: null prototype] {
      accept: [Array],
      'accept-encoding': [Array],
      'user-agent': [Array],
      host: [Array]
    },
    [Symbol(errored)]: null,
    [Symbol(kHighWaterMark)]: 16384,
    [Symbol(kRejectNonStandardBodyWrites)]: false,
    [Symbol(kUniqueHeaders)]: null
  },
  cause: Error: unexpected end of file
      at Zlib.zlibOnError [as onerror] (node:zlib:189:17) {
    errno: -5,
    code: 'Z_BUF_ERROR'
  }
}

Interestingly the request works setting the request header to "Accept-Encoding": "*" or "Accept-Encoding": "zstd" or when downgrading to e.g. 0.27.2.

Looks like it is somehow related to #5246

@ttomasini
Copy link

@DigitalBrainJS Please let me know if you need any further information to track this issue down. I'd be very happy to provide the necessary details if possible.

@DigitalBrainJS
Copy link
Collaborator

DigitalBrainJS commented May 6, 2024

@ttomasini This is a known issue in Axios 0.x. Axios 1.x fixes a batch of issues related to response decompression in node.js, including this one. (#5306, #4701)

@ttomasini
Copy link

@DigitalBrainJS Thank you for the quick answer Dmitriy! That's good to know. I just wonder why it works/worked flawlessly in v0.27.2.
So I have to inform the affected project to migrate to axios v1.x

@thithi7110
Copy link

@yuchen001
I was having the same problem.
Returning 204status would result in a pending status on the client side.

I was returning the following from the backend

return Response({}, status=status.HTTP_204_NO_CONTENT)

Then I did the following and the client worked fine.

return Response(status=status.HTTP_204_NO_CONTENT)

Not sure why it was working with previous axios.

@D2XHibinger
Copy link

bump

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants