Skip to content

Commit 0db81ac

Browse files
fix(api): /auth/github + /auth/google/callback agent-actionable missing-field copy (BUG-API-184)
POST /auth/github body `{}` used to return: error: "missing_code" message: "code field is required" Accurate, but agent-unhelpful. An LLM hitting this 4xx needed to either know the GitHub OAuth code-exchange contract already or open /openapi.json to learn what to send. Stamp the full request-body shape in the message so the agent can retry without a second round trip: "Request body is missing the required `code` field. POST `{\"code\": \"<github_oauth_code>\"}` after exchanging your OAuth authorization code at GitHub." Per rule 16 (enumerate ALL call sites), the same `"code field is required"` string lived on /auth/google/callback — mirrored the same upgrade there, plus the adjacent `missing_redirect_uri` message. Error codes (`missing_code`, `missing_redirect_uri`) stay stable so agents branching on `.error` are unaffected. Coverage block: Symptom: /auth/github POST nofields 400 — message should list required fields (BUG-API-184) Enumeration: rg -F 'code field is required' internal/ (2 sites) rg -F 'redirect_uri field is required' internal/ (1 site) rg -F 'missing_code' internal/handlers/ (2 emit sites in auth.go) Sites found: 2 missing_code emits + 1 missing_redirect_uri emit Sites touched: 3 Coverage test: TestAuth_GitHub_MissingCodeAndBadBody asserts message contains both ``code`` (the field name) and `{"code":` (the body shape). Error code unchanged so back-compat tests stay green. Live verified: pending auto-deploy + curl -s -X POST -H 'Content-Type: application/json' -d '{}' \ https://api.instanode.dev/auth/github | jq .message Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 84d0e47 commit 0db81ac

2 files changed

Lines changed: 38 additions & 3 deletions

File tree

internal/handlers/auth.go

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -408,7 +408,18 @@ func (h *AuthHandler) GitHub(c *fiber.Ctx) error {
408408
return respondError(c, fiber.StatusBadRequest, "invalid_body", "Request body must be valid JSON")
409409
}
410410
if body.Code == "" {
411-
return respondError(c, fiber.StatusBadRequest, "missing_code", "code field is required")
411+
// BUG-API-184 (QA 2026-05-29): the error message used to read
412+
// "code field is required" — accurate but agent-unhelpful. An
413+
// LLM agent that 4xx'd here needed to either know the GitHub
414+
// OAuth code-exchange contract already or open /openapi.json to
415+
// learn what to send. Stamp the full required-fields list inline
416+
// (the request body has only one field today, but the contract
417+
// shape is what the agent needs — `{ "code": "<github_oauth_code>" }`)
418+
// so the message is a self-contained instruction. Keep the
419+
// `missing_code` error code stable for back-compat (agents
420+
// branching on .error stay green).
421+
return respondError(c, fiber.StatusBadRequest, "missing_code",
422+
"Request body is missing the required `code` field. POST `{\"code\": \"<github_oauth_code>\"}` after exchanging your OAuth authorization code at GitHub.")
412423
}
413424

414425
if h.cfg.GitHubClientID == "" || h.cfg.GitHubClientSecret == "" {
@@ -514,10 +525,18 @@ func (h *AuthHandler) GoogleCallback(c *fiber.Ctx) error {
514525
return respondError(c, fiber.StatusBadRequest, "invalid_body", "Request body must be valid JSON")
515526
}
516527
if body.Code == "" {
517-
return respondError(c, fiber.StatusBadRequest, "missing_code", "code field is required")
528+
// BUG-API-184 (QA 2026-05-29): mirror the GitHub surface so the
529+
// agent-actionable message names BOTH fields the Google callback
530+
// expects (code + redirect_uri). Same code stays for back-compat;
531+
// only the human/agent-facing message gains the shape hint.
532+
return respondError(c, fiber.StatusBadRequest, "missing_code",
533+
"Request body is missing the required `code` field. POST `{\"code\": \"<google_oauth_code>\", \"redirect_uri\": \"<uri>\"}` after exchanging your OAuth authorization code at Google.")
518534
}
519535
if body.RedirectURI == "" {
520-
return respondError(c, fiber.StatusBadRequest, "missing_redirect_uri", "redirect_uri field is required")
536+
// BUG-API-184: same treatment — name the field and the body shape
537+
// so an LLM hitting this 4xx has everything it needs to retry.
538+
return respondError(c, fiber.StatusBadRequest, "missing_redirect_uri",
539+
"Request body is missing the required `redirect_uri` field. POST `{\"code\": \"<google_oauth_code>\", \"redirect_uri\": \"<uri>\"}` matching the redirect_uri you registered with Google.")
521540
}
522541

523542
accessToken, err := exchangeGoogleAuthorizationCode(c.Context(), h.cfg.GoogleClientID, h.cfg.GoogleClientSecret, body.Code, body.RedirectURI)

internal/handlers/auth_oauth_coverage_test.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,23 @@ func TestAuth_GitHub_MissingCodeAndBadBody(t *testing.T) {
248248

249249
r2 := oauthPostJSON(t, app, "/auth/github", `{}`)
250250
assert.Equal(t, http.StatusBadRequest, r2.StatusCode)
251+
// BUG-API-184 (QA 2026-05-29): the missing-code message used to read
252+
// "code field is required" — accurate but agent-unhelpful. Pin the
253+
// agent-actionable copy: the message MUST name the field AND the
254+
// expected request body shape so an LLM hitting this 4xx has
255+
// everything it needs to retry without opening the OpenAPI spec.
256+
var env struct {
257+
Error string `json:"error"`
258+
Message string `json:"message"`
259+
}
260+
require.NoError(t, json.NewDecoder(r2.Body).Decode(&env))
251261
r2.Body.Close()
262+
assert.Equal(t, "missing_code", env.Error,
263+
"BUG-API-184: error code MUST stay missing_code for back-compat")
264+
assert.Contains(t, env.Message, "`code`",
265+
"BUG-API-184: message must name the required field (`code`); got %q", env.Message)
266+
assert.Contains(t, env.Message, `{"code":`,
267+
"BUG-API-184: message must show the request body shape `{\"code\": \"<...>\"}`; got %q", env.Message)
252268
}
253269

254270
func TestAuth_GitHub_NotConfigured(t *testing.T) {

0 commit comments

Comments
 (0)