cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 
Announcements
Musicians, convert your MuseScore files to PDF to play music on the go! Learn more here.

Dropbox API Support & Feedback

Find help with the Dropbox API from other developers.

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Re: GET request to /files/download fails with "The network connection was lost"

GET request to /files/download fails with "The network connection was lost"

personalizedrefrigerator
Explorer | Level 4

Hi,

Our app uses Dropbox's HTTP API to upload and download files. Since a few days ago, some of our app's requests are failing with "The network connection was lost". This issue was first reported on May 2nd. Our app uses React Native.

 

The failing request downloads files from Dropbox using a GET request to https://content.dropboxapi.com/2/files/download (code). A simplified version of the request looks like this:

fetch("https://content.dropboxapi.com/2/files/download", {"headers":{"Dropbox-API-Arg":"{\"path\":\"/info.json\"}","Authorization":"Bearer TOKEN_HERE","Content-Type":"application/octet-stream"},"method":"GET"})

If I use POST instead of GET, the request consistently returns the content of the file. However, as commented here, using a POST request with an empty body seems to fail in React Native/iOS. Dropbox doesn't seem to allow a non-empty body for this request.

 

Sometimes, the above fetch does work. However, I can reproduce the failure consistently by sending the above request twice, but only in some environments:
 • Running code from the Safari development tools, inspecting a WebView in our application.
 • Running code from the React Native Hermes development tools.

I can't reproduce this when using the same code in a NodeJS REPL or the Firefox JavaScript console.

 

Downstream issue: https://github.com/laurent22/joplin/issues/10396

 

 

15 Replies 15

personalizedrefrigerator
Explorer | Level 4

> Also, I notice there are some other error codes "_kCFStreamErrorCodeKey=57, _kCFStreamErrorDomainKey=1". I'm not familiar with those, and we can't provide support for the platform/network client itself as it's not made by Dropbox, but do you know what those particular codes may indicate?

 

 

I don't know what the `_kCF...` error codes mean and didn't find references to these particular error codes with a web search or in the Apple documentation.


> I do see that it has "response_status=200", which doesn't seem to match the other output we've seen (where there doesn't seem to be a response), but that may be because as you noted it says "cache_hit=true", so perhaps it's actually referring to a cached response from a previous call.

 

 

It's strange that this is a cache hit — the original response contains `Cache-Control: no-cache`. It would be interesting to compare the current headers with those from a few weeks ago to see if this, or other cache-related headers changed.


We merged a workaround that, on request failure, sends a request for a different file before retrying the original request. Even though failure doesn't occur for most `files/download` GET requests, the extra API requests required by this workaround aren't ideal.

 

Forcing iOS to avoid the cache may be a better solution. As such, I've also tried:
• Passing `Cache-Control: no-cache` as an additional request header. (Doesn't work -- same "network request failed" error).
• Adding a new header and adding/referencing it in the `Vary` header (see this StackOverflow discussion). (Doesn't work -- same "network request failed" error).

• Retrying with `?` appended to the Dropbox `files/download` URL on failure. (Doesn't work -- same "network request failed" error).

• Retrying with `?retry` appended to the Dropbox `files/download` URL on failure. This results in `Error (400): Error in URL parameters: Bad query field: 'retry'`.

 

Greg-DB
Dropbox Staff

I found an old sample of a /2/files/download call from November in my records. The request didn't have the same browser/CORS headers, so it's not an exact comparison, but for reference the response did have "Cache-Control: no-cache".

personalizedrefrigerator
Explorer | Level 4

Thank you for checking!

The 200 OK response attached in a previous message has "Vary: Dropbox-API-Arg, Authorization". Based on MDN, the Vary header is related to caching and content-negotiation. Does the November response have a similar Vary header?

---
Edit:
The XCode network logs contain:

 

 

Vary Validation: (null)
     Evaluation: PASS
        Request: https://content.dropboxapi.com/<redacted>
     VaryHeader: Dropbox-API-Arg, Authorization
} [3:272075]

 

 

According to this article, the Vary header is often used to validate a cache record (to determine whether that record can be used or if the request should be treated as a cache miss).

Based on this, one possibility is that the original `Cache-Control: no-cache` causes the response to not be cached (or incorrectly cached). The next request checks the last request's "Dropbox-API-Arg" and "Authorization" because of the "Vary" header. In this case, they're the same, so the system treats this as a cache hit. However, the previous response isn't correctly cached, causing an error. Note that this is just a guess. It should be possible to test this with a local webserver, however.

Edit 2: "no-cache" means "revalidate before using the cache" while "no-store" means "don't store responses in the cache". Based on this, revalidating is expected.

Based on this, I've tested with setting the fetch `cache` to `no-store` to disable caching,

 

 

await fetch(
  "https://content.dropboxapi.com/2/files/download",
  {
    "headers": {
      "Dropbox-API-Arg": "{\"path\":\"/info.json\"}",
      "Authorization": "Bearer TOKEN_HERE",
      "Content-Type": "application/octet-stream"
    },
    "method": "GET",
    "cache": "no-store"
  }
)

 

I'm currently in the process of testing this.

personalizedrefrigerator
Explorer | Level 4

Unfortunately, "cache": "no-store" doesn't seem to help.

Based on a suggestion received elsewhere, I've also tried adding an If-None-Match set to a random value:

 

await fetch(
  "https://content.dropboxapi.com/2/files/download",
  {
    "headers": {
      "Dropbox-API-Arg": "{\"path\":\"/info.json\"}",
      "Authorization": "Bearer TOKEN_HERE",
      "Content-Type": "application/octet-stream",
      "If-None-Match": "JoplinIgnore-" + (Math.floor(Math.random() * 100000)),
    },
    "method": "GET"
  }
)

 


 Our source code already had a similar workaround for a different sync provider with the comment,

> On iOS, the network lib appends a If-None-Match header to PROPFIND calls, which is kind of correct because the call is idempotent and thus could be cached. According to RFC-7232 though only GET and HEAD should have this header for caching purposes. It makes no mention of PROPFIND.
> [... additional commenting ...]

 

This is a GET request and not a PROPFIND request. However, I suspect that passing a random `If-None-Match` overrides the iOS network library's default and forces Dropbox to return a 200 OK response (rather than a 304 Not Modified, which I think may have been Dropbox's response when the issue occurred).

 

I'm in the process of testing this. So far, however, it seems to work. This is the pull request with the relevant change.

 

Thank you for helping me debug this!

Greg-DB
Dropbox Staff

Thanks for following up and sharing this! I'm glad to hear you were able to work that out.

anmipo
Helpful | Level 6

I just faced the same error in a native iOS app, can confirm this is cache-related.

 

The app uses Apple's own URLRequest, no wrappers. When sending POST requests for the same file to "2/files/download" endpoint:

  • First request works fine and returns the file
  • The second request depends on urlRequest.cachePolicy:
    • Fails with -1005 if cache policy is not set or allows caching
    • Works out fine if set to .reloadIgnoringLocalCacheData

@personalizedrefrigerator , thank you for the hint in the right direction!

Need more support?