Skip to content

Commit 3a9eb0a

Browse files
committed
fix(api): reject >20MB single_part uploads with a clear error
UploadFile always sends mode=\"single_part\" but Notion's single-part upload limit is ~20MB; anything larger fails on the server with an opaque error. The client already knows the file size, so add an explicit SinglePartUploadMaxBytes constant and fail the upload early with a message that names the limit and notes that multi_part uploads are not yet supported. Full multipart support is out of scope for this PR and tracked as follow-up work.
1 parent 71d2800 commit 3a9eb0a

2 files changed

Lines changed: 29 additions & 0 deletions

File tree

internal/api/client.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,11 @@ func (c *Client) GetPageMarkdown(ctx context.Context, pageID string) (*PageMarkd
134134
return &out, nil
135135
}
136136

137+
// SinglePartUploadMaxBytes is Notion's single_part file-upload size limit.
138+
// Files larger than this require the multi_part upload flow, which this
139+
// client does not yet implement.
140+
const SinglePartUploadMaxBytes = 20 * 1024 * 1024
141+
137142
// UploadFile streams a file upload to the Notion API without buffering the whole
138143
// payload in memory. The reader is consumed through a multipart writer piped
139144
// directly into the HTTP request body. Pass the exact byte size so the server
@@ -146,6 +151,9 @@ func (c *Client) UploadFile(ctx context.Context, name string, size int64, body i
146151
if size <= 0 {
147152
return "", fmt.Errorf("file size must be positive")
148153
}
154+
if size > SinglePartUploadMaxBytes {
155+
return "", fmt.Errorf("file %q is %d bytes; Notion's single_part upload limit is %d bytes (~20MB) and multi_part uploads are not yet supported by this client", name, size, SinglePartUploadMaxBytes)
156+
}
149157
if body == nil {
150158
return "", fmt.Errorf("file body is required")
151159
}

internal/api/client_test.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,3 +260,24 @@ func TestTrashPageUsesPatch(t *testing.T) {
260260
t.Fatalf("TrashPage: %v", err)
261261
}
262262
}
263+
264+
func TestUploadFileRejectsOversizedSinglePart(t *testing.T) {
265+
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
266+
t.Fatalf("unexpected request: %s %s", r.Method, r.URL.Path)
267+
}))
268+
defer srv.Close()
269+
270+
client, err := NewClient(config.APIConfig{BaseURL: srv.URL + "/v1"}, "secret-token")
271+
if err != nil {
272+
t.Fatalf("NewClient: %v", err)
273+
}
274+
275+
oversize := int64(SinglePartUploadMaxBytes + 1)
276+
_, err = client.UploadFile(context.Background(), "big.png", oversize, strings.NewReader(""))
277+
if err == nil {
278+
t.Fatalf("UploadFile returned nil error; expected size-limit error")
279+
}
280+
if !strings.Contains(err.Error(), "single_part upload limit") {
281+
t.Fatalf("UploadFile error = %q, want single_part limit message", err.Error())
282+
}
283+
}

0 commit comments

Comments
 (0)