Copying binary files to sprite fails with a 400/Bad Request

Open
#116 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

Difficulty
4/5
Estimated time
3-5 days
Newbie friendliness
35/100
Issue type
Bug
Clarity
Mostly clear
Activity status
Stale
Domain
api

Research direction

Reproduce the PUT request to the /v1/sprites/{sprite}/fs/write endpoint with text and binary files, using the curl example and the VS Code extension as comparison points. Check the behavior below and above 1 MB, then determine whether the API behavior or its documentation needs updating. Done means the binary-file failure and size limit are resolved or clearly documented.

Written by the indexing model from the issue text.

Description

A PUT to https://api.sprites.dev/v1/sprites/my-cool-sprite/fs/write?path=/home/sprite/test/foo.txt works as expected, but the same request fails with any binary file I've tried (pdf, png, jpg, zip, mp4). There also appears to be an undocumented 1 megabyte file size limit (which seems awfully small?).

I'm able to replicate this behavior in the VS Code extension, so I don't believe it's necessarily tied to the specifics of my curl requests.

An example for a file under 1M (the ~test/ directory exists on the sprite):

11:49:13 ~ > curl -vv -X PUT "https://api.sprites.dev/v1/sprites/my-cool-sprite/fs/write?path=/home/sprite/test/foo.zip" -H 'Authorization: <tok>' --data-binary @foo.zip
11:49:21.484413 [0-0] * Host api.sprites.dev:443 was resolved.
11:49:21.484485 [0-0] * IPv6: 2a09:8280:1::99:d7b3:0
11:49:21.484522 [0-0] * IPv4: 169.155.48.226
11:49:21.484559 [0-0] * [HTTPS-CONNECT] adding wanted h2
11:49:21.484595 [0-0] * [HTTPS-CONNECT] added
11:49:21.484630 [0-0] * [HTTPS-CONNECT] connect, init
11:49:21.484672 [0-0] *   Trying [2a09:8280:1::99:d7b3:0]:443...
11:49:21.484756 [0-0] * Immediate connect fail for 2a09:8280:1::99:d7b3:0: Network is unreachable
11:49:21.484804 [0-0] *   Trying 169.155.48.226:443...
11:49:21.484868 [0-0] * [HTTPS-CONNECT] connect -> 0, done=0
11:49:21.484908 [0-0] * [HTTPS-CONNECT] Curl_conn_connect(block=0) -> 0, done=0
11:49:21.484943 [0-0] * [HTTPS-CONNECT] adjust_pollset -> 1 socks
11:49:21.484981 [0-0] * [HTTPS-CONNECT] connect -> 0, done=0
11:49:21.485016 [0-0] * [HTTPS-CONNECT] Curl_conn_connect(block=0) -> 0, done=0
11:49:21.485054 [0-0] * [HTTPS-CONNECT] adjust_pollset -> 1 socks
11:49:21.501439 [0-0] * ALPN: curl offers h2,http/1.1
11:49:21.501649 [0-0] * TLSv1.3 (OUT), TLS handshake, Client hello (1):
11:49:21.506231 [0-0] *  CAfile: /etc/ssl/certs/ca-certificates.crt
11:49:21.506278 [0-0] *  CApath: /etc/ssl/certs
11:49:21.506316 [0-0] * [HTTPS-CONNECT] connect -> 0, done=0
11:49:21.506356 [0-0] * [HTTPS-CONNECT] Curl_conn_connect(block=0) -> 0, done=0
11:49:21.506397 [0-0] * [HTTPS-CONNECT] adjust_pollset -> 1 socks
11:49:21.515980 [0-0] * TLSv1.3 (IN), TLS handshake, Server hello (2):
11:49:21.516175 [0-0] * TLSv1.3 (IN), TLS change cipher, Change cipher spec (1):
11:49:21.516226 [0-0] * TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
11:49:21.516268 [0-0] * TLSv1.3 (IN), TLS handshake, Certificate (11):
11:49:21.517011 [0-0] * TLSv1.3 (IN), TLS handshake, CERT verify (15):
11:49:21.517120 [0-0] * TLSv1.3 (IN), TLS handshake, Finished (20):
11:49:21.517181 [0-0] * TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):
11:49:21.517233 [0-0] * TLSv1.3 (OUT), TLS handshake, Finished (20):
11:49:21.517299 [0-0] * SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384 / x25519 / id-ecPublicKey
11:49:21.517336 [0-0] * ALPN: server accepted h2
11:49:21.517377 [0-0] * Server certificate:
11:49:21.517416 [0-0] *  subject: CN=api.sprites.dev
11:49:21.517459 [0-0] *  start date: Jan 10 23:31:00 2026 GMT
11:49:21.517497 [0-0] *  expire date: Apr 10 23:30:59 2026 GMT
11:49:21.517534 [0-0] *  subjectAltName: host "api.sprites.dev" matched cert's "api.sprites.dev"
11:49:21.517573 [0-0] *  issuer: C=US; O=Let's Encrypt; CN=E7
11:49:21.517608 [0-0] *  SSL certificate verify ok.
11:49:21.517648 [0-0] *   Certificate level 0: Public key type EC/prime256v1 (256/128 Bits/secBits), signed using ecdsa-with-SHA384
11:49:21.517686 [0-0] *   Certificate level 1: Public key type EC/secp384r1 (384/192 Bits/secBits), signed using sha256WithRSAEncryption
11:49:21.517723 [0-0] *   Certificate level 2: Public key type RSA (4096/152 Bits/secBits), signed using sha256WithRSAEncryption
11:49:21.517760 [0-0] * [HTTPS-CONNECT] connect+handshake h2: 33ms, 1st data: 31ms
11:49:21.517812 [0-0] * [HTTP/2] [0] created h2 session
11:49:21.517848 [0-0] * [HTTP/2] [0] -> FRAME[SETTINGS, len=18]
11:49:21.517883 [0-0] * [HTTP/2] [0] -> FRAME[WINDOW_UPDATE, incr=1048510465]
11:49:21.517919 [0-0] * [HTTP/2] cf_connect() -> 0, 1, 
11:49:21.517955 [0-0] * [HTTPS-CONNECT] connect -> 0, done=1
11:49:21.517989 [0-0] * [HTTPS-CONNECT] Curl_conn_connect(block=0) -> 0, done=1
11:49:21.518026 [0-0] * Connected to api.sprites.dev (169.155.48.226) port 443
11:49:21.518058 [0-0] * using HTTP/2
11:49:21.518128 [0-0] * [HTTP/2] [1] OPENED stream for https://api.sprites.dev/v1/sprites/my-cool-sprite/fs/write?path=/home/sprite/test/foo.zip
11:49:21.518166 [0-0] * [HTTP/2] [1] [:method: PUT]
11:49:21.518200 [0-0] * [HTTP/2] [1] [:scheme: https]
11:49:21.518235 [0-0] * [HTTP/2] [1] [:authority: api.sprites.dev]
11:49:21.518267 [0-0] * [HTTP/2] [1] [:path: /v1/sprites/adrien-test/fs/write?path=/home/sprite/test/foo.zip]
11:49:21.518298 [0-0] * [HTTP/2] [1] [user-agent: curl/8.14.1]
11:49:21.518332 [0-0] * [HTTP/2] [1] [accept: */*]
11:49:21.518365 [0-0] * [HTTP/2] [1] [authorization: Bearer <tok>]
11:49:21.518398 [0-0] * [HTTP/2] [1] [content-length: 317]
11:49:21.518436 [0-0] * [HTTP/2] [1] [content-type: application/x-www-form-urlencoded]
11:49:21.518474 [0-0] * [HTTP/2] [1] submit -> 666, 0
11:49:21.518513 [0-0] * [HTTP/2] [1] -> FRAME[HEADERS, len=204, hend=1, eos=0]
11:49:21.518545 [0-0] * [HTTP/2] [1] req_body_read(len=16384) eos=1 -> 317, 0
11:49:21.518579 [0-0] * [HTTP/2] [1] -> FRAME[DATA, len=317, eos=1, padlen=0]
11:49:21.518627 [0-0] * [HTTP/2] [0] egress: wrote 603 bytes
11:49:21.518661 [0-0] * [HTTP/2] [1] cf_send(len=666) -> 666, 0, eos=1, h2 windows 65218-65218 (stream-conn), buffers 0-0 (stream-conn)
11:49:21.518694 [0-0] > PUT /v1/sprites/adrien-test/fs/write?path=/home/sprite/test/foo.zip HTTP/2
11:49:21.518694 [0-0] > Host: api.sprites.dev
11:49:21.518694 [0-0] > User-Agent: curl/8.14.1
11:49:21.518694 [0-0] > Accept: */*
11:49:21.518694 [0-0] > Authorization: Bearer <tok>
11:49:21.518694 [0-0] > Content-Length: 317
11:49:21.518694 [0-0] > Content-Type: application/x-www-form-urlencoded
11:49:21.518694 [0-0] > 
11:49:21.518960 [0-0] * upload completely sent off: 317 bytes
11:49:21.518999 [0-0] * [HTTP/2] [0] progress ingress: done
11:49:21.519033 [0-0] * [HTTP/2] [1] cf_recv(len=102400) -> -1 81, window=0/65535, connection 1048576000/1048576000
11:49:21.530421 [0-0] * TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
11:49:21.530507 [0-0] * TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
11:49:21.530549 [0-0] * [HTTP/2] [0] progress ingress: done
11:49:21.530586 [0-0] * [HTTP/2] [1] cf_recv(len=102400) -> -1 81, window=0/65535, connection 1048576000/1048576000
11:49:21.531713 [0-0] * [HTTP/2] [0] ingress: read 33 bytes
11:49:21.531759 [0-0] * [HTTP/2] [0] <- FRAME[SETTINGS, len=24]
11:49:21.531796 [0-0] * [HTTP/2] [0] MAX_CONCURRENT_STREAMS: 100
11:49:21.531831 [0-0] * [HTTP/2] [0] ENABLE_PUSH: TRUE
11:49:21.531863 [0-0] * [HTTP/2] [0] progress ingress: inbufg=0
11:49:21.531906 [0-0] * [HTTP/2] [0] ingress: read 22 bytes
11:49:21.531939 [0-0] * [HTTP/2] [0] <- FRAME[SETTINGS, ack=1]
11:49:21.532012 [0-0] * [HTTP/2] [0] <- FRAME[WINDOW_UPDATE, incr=983041]
11:49:21.532049 [0-0] * [HTTP/2] [0] progress ingress: inbufg=0
11:49:21.532082 [0-0] * [HTTP/2] [0] progress ingress: done
11:49:21.532118 [0-0] * [HTTP/2] [0] -> FRAME[SETTINGS, ack=1]
11:49:21.532159 [0-0] * [HTTP/2] [0] egress: wrote 9 bytes
11:49:21.532195 [0-0] * [HTTP/2] [1] cf_recv(len=102400) -> -1 81, window=0/65536, connection 1048576000/1048576000
11:49:21.540899 [0-0] * [HTTP/2] [0] ingress: read 225 bytes
11:49:21.540944 [0-0] < HTTP/2 400 
11:49:21.540983 [0-0] * [HTTP/2] [1] local window update by 10420224
11:49:21.541023 [0-0] * [HTTP/2] [1] status: HTTP/2 400
11:49:21.541065 [0-0] < date: Tue, 27 Jan 2026 16:49:21 GMT
11:49:21.541103 [0-0] * [HTTP/2] [1] header: date: Tue, 27 Jan 2026 16:49:21 GMT
11:49:21.541142 [0-0] < content-length: 35
11:49:21.541181 [0-0] * [HTTP/2] [1] header: content-length: 35
11:49:21.541223 [0-0] < vary: accept-encoding
11:49:21.541261 [0-0] * [HTTP/2] [1] header: vary: accept-encoding
11:49:21.541304 [0-0] < content-type: application/json; charset=utf-8
11:49:21.541343 [0-0] * [HTTP/2] [1] header: content-type: application/json; charset=utf-8
11:49:21.541384 [0-0] < cache-control: max-age=0, private, must-revalidate
11:49:21.541430 [0-0] * [HTTP/2] [1] header: cache-control: max-age=0, private, must-revalidate
11:49:21.541478 [0-0] < server: Fly/3204325f5 (2026-01-26)
11:49:21.541516 [0-0] * [HTTP/2] [1] header: server: Fly/3204325f5 (2026-01-26)
11:49:21.541552 [0-0] < via: 2 fly.io, 2 fly.io
11:49:21.541594 [0-0] * [HTTP/2] [1] header: via: 2 fly.io, 2 fly.io
11:49:21.541633 [0-0] < fly-request-id: 01KG05R63PZ2TY7JCRQDB6THV9-ewr
11:49:21.541671 [0-0] * [HTTP/2] [1] header: fly-request-id: 01KG05R63PZ2TY7JCRQDB6THV9-ewr
11:49:21.541712 [0-0] * [HTTP/2] [1] <- FRAME[HEADERS, len=172, hend=1, eos=0]
11:49:21.541752 [0-0] < 
11:49:21.541788 [0-0] * [HTTP/2] [1] DRAIN select_bits=1
11:49:21.541822 [0-0] * [HTTP/2] [1] <- FRAME[DATA, len=35, eos=1, padlen=0]
11:49:21.541852 [0-0] * [HTTP/2] [1] DATA, window=35/20905984
11:49:21.541884 [0-0] * [HTTP/2] [1] CLOSED
11:49:21.541914 [0-0] * [HTTP/2] [1] DRAIN select_bits=1
11:49:21.541947 [0-0] * [HTTP/2] [0] progress ingress: inbufg=0
11:49:21.541979 [0-0] * [HTTP/2] [1] DRAIN select_bits=1
11:49:21.542012 [0-0] * [HTTP/2] [0] progress ingress: done
11:49:21.542046 [0-0] * [HTTP/2] [1] returning CLOSE
11:49:21.542080 [0-0] * [HTTP/2] handle_stream_close -> 0, 0
11:49:21.542113 [0-0] * [HTTP/2] [1] cf_recv(len=102400) -> 0 0, window=-1/-1, connection 1048575965/1048576000
11:49:21.542149 [0-0] * Connection #0 to host api.sprites.dev left intact
{"errors":{"detail":"Bad Request"}}
Dominant language
TypeScript
Stars
51
Forks
4
Avg merge
23h 57m
Merged PRs (30d)
12

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from superfly/sprites-docs

All issues in superfly/sprites-docs

Similar issues

More TypeScript issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.