[object Object]

← back to Cli Printing Press

fix(cli): default Accept to application/json instead of */* (#1229)

e40389efd8e3fcfcc673d95accdef6678c455dc9 · 2026-05-12 11:34:38 -0700 · Trevin Chow

Strict-JSON APIs (Zendesk, Atlassian REST, Salesforce) return 415 on
the first call when the Accept header isn't literally application/json.
The previous */* value was chosen for WAF presence-checks, which a
non-empty application/json also satisfies, so the bot-detection
motivation is preserved while strict APIs stop failing on the first
request. Specs that need a different content type still declare it via
RequiredHeaders or per-endpoint headerOverrides, both of which run
before the if-empty default.

Closes #1119

Files touched

Diff

commit e40389efd8e3fcfcc673d95accdef6678c455dc9
Author: Trevin Chow <trevin@trevinchow.com>
Date:   Tue May 12 11:34:38 2026 -0700

    fix(cli): default Accept to application/json instead of */* (#1229)
    
    Strict-JSON APIs (Zendesk, Atlassian REST, Salesforce) return 415 on
    the first call when the Accept header isn't literally application/json.
    The previous */* value was chosen for WAF presence-checks, which a
    non-empty application/json also satisfies, so the bot-detection
    motivation is preserved while strict APIs stop failing on the first
    request. Specs that need a different content type still declare it via
    RequiredHeaders or per-endpoint headerOverrides, both of which run
    before the if-empty default.
    
    Closes #1119
---
 internal/generator/generator_test.go                           |  8 ++++----
 internal/generator/templates/client.go.tmpl                    | 10 ++++++++--
 .../printing-press-oauth2-cc/internal/client/client.go         | 10 ++++++++--
 .../printing-press-golden/internal/client/client.go            | 10 ++++++++--
 .../tier-routing-golden/internal/client/client.go              | 10 ++++++++--
 5 files changed, 36 insertions(+), 12 deletions(-)

diff --git a/internal/generator/generator_test.go b/internal/generator/generator_test.go
index e63960c1..a83cba28 100644
--- a/internal/generator/generator_test.go
+++ b/internal/generator/generator_test.go
@@ -1500,7 +1500,7 @@ func TestGenerateBrowserChromeH3Transport(t *testing.T) {
 	// surf's Chrome impersonation manages the Accept header alongside
 	// User-Agent on the H3 transport too; the generator must not emit a
 	// competing default.
-	assert.NotContains(t, string(clientGo), `req.Header.Set("Accept", "*/*")`)
+	assert.NotContains(t, string(clientGo), `req.Header.Set("Accept",`)
 
 	runGoCommand(t, outputDir, "mod", "tidy")
 	runGoCommand(t, outputDir, "test", "./internal/client")
@@ -5994,7 +5994,7 @@ func TestGenerate_UserAgentOverrideGatedByBrowserTransport(t *testing.T) {
 	standardClient, err := os.ReadFile(filepath.Join(standardDir, "internal", "client", "client.go"))
 	require.NoError(t, err)
 	assert.Contains(t, string(standardClient), `req.Header.Set("User-Agent", "standard-pp-cli/0.1.0")`)
-	assert.Contains(t, string(standardClient), `req.Header.Set("Accept", "*/*")`)
+	assert.Contains(t, string(standardClient), `req.Header.Set("Accept", "application/json")`)
 	assert.Contains(t, string(standardClient), `if req.Header.Get("Accept") == "" {`)
 
 	browserDir := filepath.Join(t.TempDir(), "browser-pp-cli")
@@ -6006,7 +6006,7 @@ func TestGenerate_UserAgentOverrideGatedByBrowserTransport(t *testing.T) {
 	assert.NotContains(t, string(browserClient), `req.Header.Set("User-Agent"`)
 	// surf's Chrome impersonation manages the Accept header alongside
 	// User-Agent; the generator must not emit a competing default for either.
-	assert.NotContains(t, string(browserClient), `req.Header.Set("Accept", "*/*")`)
+	assert.NotContains(t, string(browserClient), `req.Header.Set("Accept",`)
 	// auth.go is not emitted for auth.type:none specs (see Generator.renderAuthFiles).
 	// Stronger assertion than the previous "no newAuthRefreshCmd in auth.go": there's
 	// no auth.go at all for no-auth CLIs.
@@ -6028,7 +6028,7 @@ func TestGenerateRequiredAcceptHeaderBeatsDefaultAccept(t *testing.T) {
 
 	clientSrc := readGeneratedFile(t, outputDir, "internal", "client", "client.go")
 	requiredIdx := strings.Index(clientSrc, `req.Header.Set("Accept", "application/vnd.api.v3+json")`)
-	defaultIdx := strings.Index(clientSrc, `req.Header.Set("Accept", "*/*")`)
+	defaultIdx := strings.Index(clientSrc, `req.Header.Set("Accept", "application/json")`)
 	require.GreaterOrEqual(t, requiredIdx, 0, "spec-required Accept header must be emitted")
 	require.GreaterOrEqual(t, defaultIdx, 0, "Accept fallback default must still be emitted")
 	assert.Less(t, requiredIdx, defaultIdx, "spec-required Accept must be set before the if-empty default")
diff --git a/internal/generator/templates/client.go.tmpl b/internal/generator/templates/client.go.tmpl
index 6aa2c7d8..34e0b973 100644
--- a/internal/generator/templates/client.go.tmpl
+++ b/internal/generator/templates/client.go.tmpl
@@ -867,9 +867,15 @@ func (c *Client) do(method, path string, params map[string]string, body any, hea
 		// stdlibs always send it. Fingerprint-checking WAFs (Imperva, Akamai,
 		// Cloudflare bot-mode, DataDome) flag the absence as a bot signal
 		// and answer with empty-body 5xx, 403, or a challenge redirect
-		// depending on vendor and rule tier.
+		// depending on vendor and rule tier. The value is application/json
+		// rather than */* because strict-JSON APIs (Zendesk, Atlassian REST,
+		// Salesforce) return 415 on anything that isn't literally
+		// application/json; specs that need a different content type
+		// (vendor mediatypes, XML, HTML) declare it via RequiredHeaders or
+		// per-endpoint headerOverrides, both of which run before this
+		// if-empty default.
 		if req.Header.Get("Accept") == "" {
-			req.Header.Set("Accept", "*/*")
+			req.Header.Set("Accept", "application/json")
 		}
 {{- end}}
 
diff --git a/testdata/golden/expected/generate-golden-api-oauth2-cc/printing-press-oauth2-cc/internal/client/client.go b/testdata/golden/expected/generate-golden-api-oauth2-cc/printing-press-oauth2-cc/internal/client/client.go
index 4bdda627..8c443678 100644
--- a/testdata/golden/expected/generate-golden-api-oauth2-cc/printing-press-oauth2-cc/internal/client/client.go
+++ b/testdata/golden/expected/generate-golden-api-oauth2-cc/printing-press-oauth2-cc/internal/client/client.go
@@ -257,9 +257,15 @@ func (c *Client) do(method, path string, params map[string]string, body any, hea
 		// stdlibs always send it. Fingerprint-checking WAFs (Imperva, Akamai,
 		// Cloudflare bot-mode, DataDome) flag the absence as a bot signal
 		// and answer with empty-body 5xx, 403, or a challenge redirect
-		// depending on vendor and rule tier.
+		// depending on vendor and rule tier. The value is application/json
+		// rather than */* because strict-JSON APIs (Zendesk, Atlassian REST,
+		// Salesforce) return 415 on anything that isn't literally
+		// application/json; specs that need a different content type
+		// (vendor mediatypes, XML, HTML) declare it via RequiredHeaders or
+		// per-endpoint headerOverrides, both of which run before this
+		// if-empty default.
 		if req.Header.Get("Accept") == "" {
-			req.Header.Set("Accept", "*/*")
+			req.Header.Set("Accept", "application/json")
 		}
 
 		resp, err := c.HTTPClient.Do(req)
diff --git a/testdata/golden/expected/generate-golden-api/printing-press-golden/internal/client/client.go b/testdata/golden/expected/generate-golden-api/printing-press-golden/internal/client/client.go
index d3c44d73..5630e4ff 100644
--- a/testdata/golden/expected/generate-golden-api/printing-press-golden/internal/client/client.go
+++ b/testdata/golden/expected/generate-golden-api/printing-press-golden/internal/client/client.go
@@ -251,9 +251,15 @@ func (c *Client) do(method, path string, params map[string]string, body any, hea
 		// stdlibs always send it. Fingerprint-checking WAFs (Imperva, Akamai,
 		// Cloudflare bot-mode, DataDome) flag the absence as a bot signal
 		// and answer with empty-body 5xx, 403, or a challenge redirect
-		// depending on vendor and rule tier.
+		// depending on vendor and rule tier. The value is application/json
+		// rather than */* because strict-JSON APIs (Zendesk, Atlassian REST,
+		// Salesforce) return 415 on anything that isn't literally
+		// application/json; specs that need a different content type
+		// (vendor mediatypes, XML, HTML) declare it via RequiredHeaders or
+		// per-endpoint headerOverrides, both of which run before this
+		// if-empty default.
 		if req.Header.Get("Accept") == "" {
-			req.Header.Set("Accept", "*/*")
+			req.Header.Set("Accept", "application/json")
 		}
 
 		resp, err := c.HTTPClient.Do(req)
diff --git a/testdata/golden/expected/generate-tier-routing-api/tier-routing-golden/internal/client/client.go b/testdata/golden/expected/generate-tier-routing-api/tier-routing-golden/internal/client/client.go
index ad913d8a..f5c5ddc4 100644
--- a/testdata/golden/expected/generate-tier-routing-api/tier-routing-golden/internal/client/client.go
+++ b/testdata/golden/expected/generate-tier-routing-api/tier-routing-golden/internal/client/client.go
@@ -354,9 +354,15 @@ func (c *Client) do(method, path string, params map[string]string, body any, hea
 		// stdlibs always send it. Fingerprint-checking WAFs (Imperva, Akamai,
 		// Cloudflare bot-mode, DataDome) flag the absence as a bot signal
 		// and answer with empty-body 5xx, 403, or a challenge redirect
-		// depending on vendor and rule tier.
+		// depending on vendor and rule tier. The value is application/json
+		// rather than */* because strict-JSON APIs (Zendesk, Atlassian REST,
+		// Salesforce) return 415 on anything that isn't literally
+		// application/json; specs that need a different content type
+		// (vendor mediatypes, XML, HTML) declare it via RequiredHeaders or
+		// per-endpoint headerOverrides, both of which run before this
+		// if-empty default.
 		if req.Header.Get("Accept") == "" {
-			req.Header.Set("Accept", "*/*")
+			req.Header.Set("Accept", "application/json")
 		}
 
 		resp, err := c.HTTPClient.Do(req)

← f1246ee1 fix(cli): skip dogfood --live error_path probe for mutating  ·  back to Cli Printing Press  ·  fix(skills): generate absolute manuscript URLs in publish PR a8ad9820 →