feat(REL-12754): Agent friendly error handling#661
feat(REL-12754): Agent friendly error handling#661nieblara wants to merge 1 commit intoREL-12752-tty-1from
Conversation
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 2 potential issues.
Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.
| errMap["suggestion"] = suggestion | ||
| } | ||
| resp, _ := json.Marshal(errMap) | ||
| return body, errors.NewError(string(resp)) |
There was a problem hiding this comment.
Empty-body error path returns empty bytes instead of synthesized JSON
Low Severity
When the HTTP response has no body, the function synthesizes a proper JSON error into resp but returns the original empty body as the first return value. In contrast, the non-empty body path reassigns body to the enriched JSON before returning it. This means a caller inspecting the first return value gets enriched JSON in one case and empty bytes in the other. The fix would be to return resp instead of body on that line.
Additional Locations (1)
| } else { | ||
| if _, exists := errMap["statusCode"]; !exists { | ||
| errMap["statusCode"] = res.StatusCode | ||
| } |
There was a problem hiding this comment.
Nil map panic when server returns JSON null body
Medium Severity
If the server returns the literal JSON value null as the error body, json.Unmarshal succeeds (returns nil error) but sets errMap to nil. The code then enters the else branch and attempts to write to the nil map at errMap["statusCode"] = res.StatusCode, which causes a runtime panic. Reading a nil map is safe in Go, but writing to one is not.
Additional Locations (1)
JackDanger
left a comment
There was a problem hiding this comment.
Great improvement — actionable errors are one of the highest-leverage changes for both agents and humans. The suggestion registry is a smart pattern. Some things to consider:
1. Error JSON shape change is a breaking contract for agent-prompts consumers
The launchdarkly-labs/agent-prompts repo (the official LD agent guide) teaches agents to check errors like this:
echo "$RESULT" | jq -e '.code == 401' > /dev/nullBefore this PR, the code field could be a numeric-ish string or omitted. After this PR, it's always a snake_case string like "unauthorized", and the numeric value moves to statusCode. This will break that exact pattern. The agent-prompts repo needs to be updated alongside this PR, or at minimum the PR description should call out the shape change explicitly.
2. baseURI extraction from the request path is fragile
if parsed, err := url.Parse(path); err == nil && parsed.Scheme != "" {
baseURI = parsed.Scheme + "://" + parsed.Host
}If path is ever a relative path (e.g., /api/v2/flags/my-proj), baseURI will be empty, and the 401 suggestion will contain a broken link: /settings/authorization with no host. Worth adding a fallback to the configured --base-uri value, or at minimum a test that exercises the relative-path case.
3. The suggestion for 404 could mislead
"Use
ldcli projects listorldcli flags listto see available resources."
If I'm working with segments, environments, metrics, or any of the ~375 auto-generated resource commands, this suggestion is wrong. Could the suggestion include the resource type from the command context? e.g., "Use ldcli segments list" when the failing command was ldcli segments get.
4. Missing suggestion for 400 (Bad Request)
This is arguably the most common error in practice — malformed JSON payloads, invalid field values, missing required fields. The API often returns helpful message text for 400s, but a suggestion like "Check the --data payload format. Use ldcli <resource> create --help for required fields." would help agents self-correct.
5. Non-JSON error bodies get the raw HTML/text stuffed into message
"message": string(body), // Could be "<html><body>Bad Gateway</body></html>"This is tested and intentional, which is fine. But for agents, an HTML blob in the message field burns tokens. Consider truncating or sanitizing the message when the body isn't JSON (e.g., first 200 chars + ...).
6. Cursor Bugbot flagged 2 issues — worth checking what those were before merging.


Make every error message actionable: preserve the HTTP status code, and suggest what to do next. Today, many errors lose their status code entirely and users see "unknown error occurred."
Three phases: (1) stop losing status codes by always producing valid JSON errors, (2) add a suggestion registry mapping status codes to guidance, (3) update plaintext error formatting to include suggestions.
Note
Medium Risk
Changes the shape and formatting of CLI error responses by always emitting JSON with
statusCodeand sometimessuggestion, which may affect downstream parsing and user-facing output.Overview
Improves CLI API error handling so error bodies always retain the HTTP
statusCodeand (for common cases like401/403/404/409/429) include an actionablesuggestionmessage.ResourcesClient.MakeRequestnow normalizes empty or non-JSON error bodies into synthetic JSON (code,message,statusCode) and injects suggestions based on status; plaintext error rendering (ErrorPlaintextOutputFn) appends aSuggestion:line when present and is hardened to avoid panics on non-string fields. Test coverage is expanded across client, output, and suggestion mapping behaviors.Written by Cursor Bugbot for commit 733df0d. This will update automatically on new commits. Configure here.
Related Jira issue: REL-12754: Better error messages with actionable suggestions