From 0cb30d9f09783844d042f12b7ea1c6e0fbaec8b1 Mon Sep 17 00:00:00 2001 From: Manas Srivastava Date: Thu, 4 Jun 2026 00:54:12 +0530 Subject: [PATCH] fix(handlers): canonical error envelope (request_id) on 3 agent-facing branches MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three agent-facing error branches emitted a bespoke envelope missing request_id (and one used a non-standard "msg" key), so an agent couldn't correlate the failure to logs/support the way every respondError path lets it: - onboarding.go ClaimPreview invalid_token (not-found): routed through respondError — adds request_id, swaps the bespoke "msg" for the canonical "message" key. The `error` keyword (what clients match on) is unchanged. - env_policy.go Put owner_required 403: added request_id alongside the existing role/allowed_roles/agent_action fields. - resource.go respondPauseUpgradeRequired 402: added request_id alongside the existing upgrade_url/agent_action. No contract break — only additive (request_id) plus the msg→message rename on a branch whose documented shape was just {ok,error}. All three changed lines covered by existing tests (claim-preview not-found, env-policy non-owner, pause-tier-wall); verified locally against real Postgres+Redis. Co-Authored-By: Claude Opus 4.8 (1M context) --- internal/handlers/env_policy.go | 1 + internal/handlers/onboarding.go | 9 ++++----- internal/handlers/resource.go | 1 + 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/internal/handlers/env_policy.go b/internal/handlers/env_policy.go index 5a052828..edf59d4c 100644 --- a/internal/handlers/env_policy.go +++ b/internal/handlers/env_policy.go @@ -100,6 +100,7 @@ func (h *EnvPolicyHandler) Put(c *fiber.Ctx) error { "role": role, "allowed_roles": []string{middleware.RoleOwner}, "agent_action": newAgentActionOwnerRequired(role), + "request_id": middleware.GetRequestID(c), }) } diff --git a/internal/handlers/onboarding.go b/internal/handlers/onboarding.go index b923073f..8065505f 100644 --- a/internal/handlers/onboarding.go +++ b/internal/handlers/onboarding.go @@ -96,11 +96,10 @@ func (h *OnboardingHandler) ClaimPreview(c *fiber.Ctx) error { if err != nil { var notFound *models.ErrOnboardingNotFound if errors.As(err, ¬Found) { - return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ - "ok": false, - "error": "invalid_token", - "msg": "Token not recognized", - }) + // Canonical envelope: respondError adds request_id and uses the + // standard "message" key (this branch previously emitted a bespoke + // "msg" field with no request_id — agents couldn't correlate it). + return respondError(c, fiber.StatusBadRequest, "invalid_token", "Token not recognized") } slog.Error("onboarding.claim_preview.db_error", "error", err, "request_id", requestID) return respondError(c, fiber.StatusServiceUnavailable, "lookup_failed", "Failed to verify token") diff --git a/internal/handlers/resource.go b/internal/handlers/resource.go index eff16992..54eeb04b 100644 --- a/internal/handlers/resource.go +++ b/internal/handlers/resource.go @@ -805,6 +805,7 @@ func respondPauseUpgradeRequired(c *fiber.Ctx, currentTier string) error { "message": "Pausing resources requires the Pro plan or higher. Your team is on the " + currentTier + " plan.", "upgrade_url": "https://instanode.dev/pricing", "agent_action": AgentActionPauseRequiresPro, + "request_id": middleware.GetRequestID(c), }) return ErrResponseWritten }