← 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
M internal/generator/generator_test.goM internal/generator/templates/client.go.tmplM testdata/golden/expected/generate-golden-api-oauth2-cc/printing-press-oauth2-cc/internal/client/client.goM testdata/golden/expected/generate-golden-api/printing-press-golden/internal/client/client.goM testdata/golden/expected/generate-tier-routing-api/tier-routing-golden/internal/client/client.go
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 →