Skip to content

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

@drien

Description

@drien

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"}}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions