Skip to finding
All findings

Finding · Egnyte · APIs · Backups

Egnyte's file API: bad names cut big uploads off, and a move onto an existing file merges it

Egnyte rejects file names with Windows-reserved characters, but its API doesn't say so. Small uploads get a 409 that the docs reserve for name clashes; uploads of 4 MB or more have the connection cut mid-transfer, so the client sees a network error. Separately, moving a file onto an existing file returns 200 and folds both into one version history, and the newer one wins.

Ryan McMillan · Found Oct 20, 2026 · Published Oct 22, 2026

Affected

Service
Egnyte Public API v1, path-based endpoints (/pubapi/v1/fs-content, /pubapi/v1/fs)
Account
One Egnyte business domain, tested October 22, 2026
Client
Rust, reqwest 0.13.5 (for the cut-off uploads)
Names
Any file name containing " : < > | * ? or \

01

What happens

Egnyte doesn't allow file names with the Windows-reserved characters " : < > | * ? \. Its help desk says so for the desktop and migration apps. The API documentation doesn't list them, and the API doesn't return a clear error when you send one:

  • A small upload (POST /pubapi/v1/fs-content/...) gets 409 Conflict. The API docs give 409 as "file or folder with the same name already exists", so it reads as a clash with a file that isn't there.
  • An upload of about 4 MB or more never gets a status at all. About three seconds in, the connection is closed while the client is still sending, and the client reports a transport failure. A correctly named 20 MB file took 14 seconds to upload on the same connection, so the server is ending the request early, not timing out.
  • A backslash is rejected earlier. Even a metadata lookup (GET /pubapi/v1/fs/...) for that name returns 400.

A dropped connection is what a flaky network looks like, so a backup job sensibly stops or retries, and retrying never helps. Mine stopped two nights in a row on a 5.9 GB video whose name contained a double quote, while files of 5.6 GB with ordinary names uploaded fine.

Move onto an existing file. POST /pubapi/v1/fs/<source> with {"action":"move","destination":"<existing file>"} returns 200. It doesn't refuse and it doesn't simply overwrite. The source disappears and becomes part of the destination's version history, and whichever file is newer becomes the current version. If the file you moved is older than the one already there, the move "succeeds" but its content survives only as a previous version. If it's newer, it looks like an overwrite.

Quota headers. X-Accesstoken-Quota-Current is documented as the queries per day you've used, but each call type counts separately. A user-info call and a folder listing each report their own running total, so the header from one call doesn't show your total for the day.

02

Evidence

Each size was uploaded twice under a name containing a double quote. The same pattern held for a colon. A test build of my client recorded why each write failed. Nothing was stored for any of the failures:

upload with a double quote in the name
size     result                                         time
1 MB     409 Conflict                                   3.4 s
2 MB     409 Conflict                                   3.4 s
3 MB     409 Conflict                                   3.6 s, 3.0 s
4 MB     transport failure while sending the body       3.3 s, 2.7 s
5 MB     transport failure while sending the body       3.2 s
10 MB    transport failure while sending the body       2.9 s
20 MB    transport failure while sending the body       4.0 s, 3.2 s
200 MB   transport failure while sending the body
20 MB, ordinary name: uploaded, checksum verified    14.1 s

Two moves onto an existing file, read back with GET /pubapi/v1/fs/<path>:

move onto an existing file
older source -> newer destination   move: 200
  current version: destination's content   (16 bytes, 01:53:03 GMT)
  versions[0]:     the moved file          (18 bytes, 01:53:00 GMT)

newer source -> older destination   move: 200
  current version: the moved file          (15 bytes, 01:54:15 GMT)
  versions[0]:     destination's content   (13 bytes, 01:54:07 GMT)
num_versions: 2 in both cases

For the quota headers, alternating calls returned user-info counts of 2 then 3 while folder listings in between returned 1, 2 and 3.

03

Reproduce

  1. 01Create a 1 MB file and a 10 MB file, and upload each to a test folder as a"b.bin (the quote URL-encoded as %22). The 1 MB upload gets 409. The 10 MB upload ends with a connection reset or closed connection instead of a status.
  2. 02Upload the 10 MB file again under an ordinary name. It succeeds, which rules out size and network.
  3. 03Upload old.txt, wait a few seconds, then upload new.txt. Move old.txt onto new.txt with {"action":"move","destination":"/<folder>/new.txt"}. You get 200 and old.txt is gone.
  4. 04Download new.txt: it still has its own content. GET /pubapi/v1/fs/<folder>/new.txt shows num_versions: 2, with old.txt's content under versions.

04

Fix and cautions

  • Clean names before uploading. Egnyte's Migration App replaces each reserved character with an underscore. I map " to ', : to - and the rest to _. Egnyte's help desk lists more rules (no trailing period, no leading or trailing spaces, no control characters, no 4-byte Unicode) that I haven't tested through the API.
  • If the same file fails with a network error every time while others upload fine, check its name before blaming the network.
  • Before a move, look up the destination. Go ahead only if it returns 404, or re-check num_versions afterwards. If you need a true overwrite, upload to the destination and then delete the source.
  • Count your own API calls rather than budgeting from X-Accesstoken-Quota-Current.

05

Not tested

  • One Egnyte business domain, tested on October 22, 2026, with path-based v1 endpoints only. I haven't tested the v2 endpoints, ID-based moves, folder moves or chunked uploads.
  • The cut-off happened between 3 and 4 MB on one home internet connection. The exact point probably depends on buffering, and other HTTP clients may still read the 409 when the server answers early. I haven't tested curl or Python.
  • I didn't set Last-Modified on uploads, so upload time and modification time were the same. I don't know which one decides the current version after a move.
  • I saw the quota counters split between user-info and folder-listing calls only. I haven't tested whether the daily cap applies to each counter or to the total.

Updates

  • Oct 22, 2026Published.