Skip to content

Snowflake chat results in 400 error #692

Description

@robert-norberg
library(ellmer)

# you'll have to trust me that I've gotten these right.
# Just setting SNOWFLAKE_ACCOUNT, SNOWFLAKE_USER, and SNOWFLAKE_PRIVATE_KEY environment variables doesn't work for orgs that use Snowflake PrivateLink.
# I can open a separate issue and a PR to go with it later.
auth_headers <- ... 

chat <- chat_snowflake(
  system_prompt = NULL,
  account = "our.privatelink.baseurl",
  credentials = auth_headers,
  model = "claude-3-5-sonnet"
)
httr2::with_verbosity({
  chat$chat("Tell me a joke in the form of a SQL query.", echo = FALSE)},
  verbosity = 3
)
*  Connected to our.privatelink.baseurl.snowflakecomputing.com (**.***.***.***) port 443 (#2)
*  ALPN, offering h2
*  ALPN, offering http/1.1
*   CAfile: /etc/ssl/certs/ca-certificates.crt
*   CApath: /etc/ssl/certs
*  TLSv1.0 (OUT), TLS header, Certificate Status (22):
*  TLSv1.3 (OUT), TLS handshake, Client hello (1):
*  TLSv1.2 (IN), TLS header, Certificate Status (22):
*  TLSv1.3 (IN), TLS handshake, Server hello (2):
*  TLSv1.2 (IN), TLS header, Certificate Status (22):
*  TLSv1.2 (IN), TLS handshake, Certificate (11):
*  TLSv1.2 (IN), TLS header, Certificate Status (22):
*  TLSv1.2 (IN), TLS handshake, Server key exchange (12):
*  TLSv1.2 (IN), TLS header, Certificate Status (22):
*  TLSv1.2 (IN), TLS handshake, Server finished (14):
*  TLSv1.2 (OUT), TLS header, Certificate Status (22):
*  TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
*  TLSv1.2 (OUT), TLS header, Finished (20):
*  TLSv1.2 (OUT), TLS change cipher, Change cipher spec (1):
*  TLSv1.2 (OUT), TLS header, Certificate Status (22):
*  TLSv1.2 (OUT), TLS handshake, Finished (20):
*  TLSv1.2 (IN), TLS header, Finished (20):
*  TLSv1.2 (IN), TLS header, Certificate Status (22):
*  TLSv1.2 (IN), TLS handshake, Finished (20):
*  SSL connection using TLSv1.2 / ECDHE-RSA-AES128-GCM-SHA256
*  ALPN, server did not agree to a protocol
*  Server certificate:
*   <redacted>
*   SSL certificate verify ok.
*  TLSv1.2 (OUT), TLS header, Supplemental data (23):
-> POST /api/v2/cortex/inference:complete HTTP/1.1
-> Host: our.privatelink.baseurl.snowflakecomputing.com
-> User-Agent: posit_workbench_rstudio
-> Accept: */*
-> Accept-Encoding: deflate, gzip, br, zstd
-> Authorization: <REDACTED>
-> X-Snowflake-Authorization-Token-Type: <REDACTED>
-> Content-Type: application/json
-> Content-Length: 229
-> 
>> {
>>   "messages": [
>>     {
>>       "role": "user",
>>       "content": "Tell me a joke in the form of a SQL query.",
>>       "content_list": [
>>         {
>>           "type": "text",
>>           "text": "Tell me a joke in the form of a SQL query."
>>         }
>>       ]
>>     }
>>   ],
>>   "model": "claude-3-5-sonnet",
>>   "stream": false
>> }
*  TLSv1.2 (IN), TLS header, Supplemental data (23):
*  Mark bundle as not supporting multiuse
<- HTTP/1.1 400 Bad Request
<- Date: Thu, 31 Jul 2025 19:07:03 GMT,Thu, 31 Jul 2025 19:07:04 GMT
<- Content-Type: application/json;charset=utf-8
<- X-Accel-Buffering: no
<- Cache-Control: no-cache
<- X-Snowflake-Request-Id: ********-****-****-****-********
<- Server: SF-LB
<- X-Envoy-Attempt-Count: 1
<- X-Envoy-Upstream-Service-Time: 1036
<- X-Content-Type-Options: nosniff
<- X-Xss-Protection: 1; mode=block
<- Expect-Ct: enforce, max-age=3600
<- Strict-Transport-Security: max-age=31536000
<- X-Frame-Options: deny
<- Content-Encoding: gzip
<- Vary: Accept-Encoding
<- Transfer-Encoding: chunked
<- 
*  Connection #2 to host our.privatelink.baseurl.snowflakecomputing.com left intact
<< {"message":"invalid request parameters: contentList text block cannot be used with content","request_id":"********-****-****-****-********"}
<< 

I'm not 100% sure what this means, but it mentions contentList and the "content_list": [...] part of the request body doesn't seem to match what Snowflake is expecting.

When I build the request body myself, omitting the "content_list": [...] part, I get a 200 back from Snowflake:

resp <- httr2::request(base_url = "https://our.privatelink.baseurl.snowflakecomputing.com") |>
  httr2::req_headers_redacted(unlist(auth_headers)) |>
  httr2::req_url_path("api/v2/cortex/inference:complete") |>
  httr2::req_body_json(data = list(
    "model" = "claude-3-5-sonnet",
    "messages" = list(
      list("role" = "user", "content" = "Tell me a joke in the form of a SQL query.")
    )
  )) |>
  httr2::req_perform()

resp |> httr2::resp_status()
[1] 200

I can't really tell where the extra "content_list": [...] part of the request body is coming from because I'm not really familiar with the S7 object oriented stuff going on in provider-snowflake.R, but I think that's the issue.

Update: If I get rid of the "content": "Tell me a joke in the form of a SQL query." part of the request body and keep the "content_list": [...] part, that also results in a 200 response. It just seems that a message must have either content or content_list, but it can't have both.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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