Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 8 additions & 5 deletions dotnet/EcencyApi/Handlers/WalletApi.Engine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -180,14 +180,17 @@ public static async Task<JsonArray> FetchEngineRewards(string username)
var response = await EngineRewardsRequest(username,
new[] { new KeyValuePair<string, string?>("hive", "1") });

// A non-object body (unparseable, or valid JSON that isn't an object) means a
// degraded upstream: keep throwing so the catch below logs it.
if (response.Json is not JsonObject obj)
throw new Exception("No rewards data returned");

var rawValues = obj.Select(kv => kv.Value).ToList();
if (rawValues.Count == 0) throw new Exception("No rewards data returned");
throw new Exception("Unexpected rewards payload");
Comment on lines 185 to +186

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Object Errors Still Disappear

This check only sends non-object payloads through the catch path. If the rewards service returns an object-shaped error body like { "error": "rate limit" } or { "message": "Internal Server Error" }, it passes this branch, the loop feeds the string value into HiveEngine.ConvertRewardsStatus, and pendingToken stays null so the result is filtered down to an empty rewards array. Callers then receive “no rewards” with no error log, even though the upstream response was degraded. Please validate the expected reward-entry shape, or the upstream status, while keeping only an actually empty object as the no-rewards case.

Fix in Claude Code

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one is pre-existing behavior, not something this PR introduces — main does exactly the same thing with an object-shaped error body.

main's only guard was rawValues.Count == 0. A body like {"error": "rate limit"} has one key, so it never hit that throw either: it went through the same loop, ConvertRewardsStatus returned nulls (JsVal.Prop on a non-object yields null, so pendingToken is null and pendingRewards is NaN — no exception), the pendingToken is > 0 filter dropped it, and the caller got an empty array with no log. Identical before and after.

The only guard this PR removes is the Count == 0 one, i.e. the genuinely-empty object. The path you're describing is untouched by the diff, so there is nothing here to regress.

Worth saying the underlying observation is fair: an object-shaped error body does read as "no rewards". But that has always been true, and closing it means validating the reward-entry shape or the upstream status — a behavior change beyond a log-noise fix, and one that needs to be weighed against the response-parity contract this service maintains. Leaving it out of scope here rather than smuggling it in.


// An empty object is NOT a failure — it is what the upstream returns for an
// account with no Hive-Engine activity, which is the common case. It falls
// through the loop below to the same empty result the catch used to produce,
// minus an error log on every such request.
var filtered = new JsonArray();
foreach (var raw in rawValues)
foreach (var raw in obj.Select(kv => kv.Value))
{
var converted = HiveEngine.ConvertRewardsStatus(raw);
var pendingToken = JsVal.AsNumber(converted["pendingToken"]);
Expand Down
Loading