Skip to content

Conversation

@Melonps
Copy link

@Melonps Melonps commented Nov 21, 2025

Which problem is this PR solving?

Description of the changes

In packages/jaeger-ui/src/api/jaeger.js

  • Validate response body for errors array even when status is 200
  • Throw error if errors are present to trigger error handling flow

After this change, I checked to show valid error message in UI, also prevents infinity loading.
Please see the screenshot below:

image

How was this change tested?

  • Add 1 test to verify this changing in packages/jaeger-ui/src/api/jaeger.test.js

Checklist

@Melonps Melonps requested a review from a team as a code owner November 21, 2025 15:49
@Melonps Melonps requested review from albertteoh and removed request for a team November 21, 2025 15:49
@graphite-app
Copy link
Contributor

graphite-app bot commented Nov 21, 2025

How to use the Graphite Merge Queue

Add the label merge-queue to this PR to add it to the merge queue.

You must have a Graphite account in order to use the merge queue. Sign up using this link.

An organization admin has enabled the Graphite Merge Queue in this repository.

Please do not merge from GitHub as this will restart CI on PRs being processed by the merge queue.

@Melonps Melonps changed the title fix: handle errors in response body with status code is 200 Handle errors in response body with status code is 200 Nov 22, 2025
@Melonps Melonps changed the title Handle errors in response body with status code is 200 fix(bug): Handle errors in response body with status code is 200 Nov 24, 2025
if (response.status < 400) {
return response.json();
return response.json().then(body => {
if (body && Array.isArray(body.errors) && body.errors.length > 0) {
Copy link
Member

Choose a reason for hiding this comment

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

A function called getJSON cannot assume to know the structure of the response. Why do we need to throw exception in this lower level function instead of handling the error where needed?

Copy link
Author

Choose a reason for hiding this comment

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

Thanks for your feedback🙏
I've refactored the code to keep getJSON generic, and moved the Jaeger specific error handling (where 200 responses can contain an errors array) into the searchTraces method itself.

Without this check, these errors would be silently ignored and treated as successful 200 responses.

error.httpBody = JSON.stringify(body, null, 2);
throw error;
}
return body;
Copy link
Member

Choose a reason for hiding this comment

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

A successful result is not mutually exclusive with some errors being present. This logic is incorrect. The errors need to be checked by the caller.

Copy link
Author

@Melonps Melonps Nov 25, 2025

Choose a reason for hiding this comment

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

@yurishkuro
Thanks for the review ! I understand your point now🙇

I see that fetchMultipleTraces already handles this pattern correctly in packages/jaeger-ui/src/reducers/trace.js, where it processes both payload.data and payload.errors separately. I should follow the same approach for searchTraces, right?

I'll need a bit more time to fix it. Will update the PR soon.

@Melonps
Copy link
Author

Melonps commented Nov 25, 2025

@yurishkuro

I misidentified the root cause. The actual problem is a trace ID normalization mismatch:

  1. Frontend sends requests using short trace IDs (e.g., "1", "xyz").
  2. Backend zero-pads them to 32-char: "0000000000000001", "0000000000000xyz", as defined in jaeger-idl
  3. Frontend stores responses under the normalized IDs
  4. Original short IDs remain stuck in FETCH_LOADING state

I think we should normalize trace IDs to 32-char hex strings on the frontend before issuing API requests by 0, since the Jaeger backend zero-pads them and we need the request and response IDs to align.

image

@yurishkuro
Copy link
Member

Where does the frontend get the short id from in the first place?

Comment on lines 14 to 16
* normalizeTraceId('1') // '0000000000000001'
* normalizeTraceId('xyz') // '0000000000000abc'
* normalizeTraceId('abcd1234') // 'abcd1234'
Copy link
Contributor

Choose a reason for hiding this comment

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

The JSDoc examples are incorrect and misleading:

  1. Line 15: normalizeTraceId('xyz') claims to return '0000000000000abc', but 'xyz' contains no valid hex characters. After cleaning (line 25), it becomes an empty string, triggering line 28-29 to return the original 'xyz' unchanged.

  2. Line 16: normalizeTraceId('abcd1234') claims to return 'abcd1234', but the function pads to 16 characters (line 33), so it should return '00000000abcd1234'.

Fix the documentation:

 * @example
 * normalizeTraceId('1') // '0000000000000001'
 * normalizeTraceId('xyz') // 'xyz' (invalid, returned unchanged)
 * normalizeTraceId('abcd1234') // '00000000abcd1234'
 * normalizeTraceId('1234567890abcdef1234') // '1234567890abcdef1234'
Suggested change
* normalizeTraceId('1') // '0000000000000001'
* normalizeTraceId('xyz') // '0000000000000abc'
* normalizeTraceId('abcd1234') // 'abcd1234'
* normalizeTraceId('1') // '0000000000000001'
* normalizeTraceId('xyz') // 'xyz' (invalid, returned unchanged)
* normalizeTraceId('abcd1234') // '00000000abcd1234'
* normalizeTraceId('1234567890abcdef1234') // '1234567890abcdef1234'

Spotted by Graphite Agent

Fix in Graphite


Is this helpful? React 👍 or 👎 to let us know.

return <Search addonBefore="Select by Trace ID" enterButton onSearch={selectTrace} />;
const handleSearch = React.useCallback(
(value: string) => {
selectTrace(normalizeTraceId(value));
Copy link
Member

Choose a reason for hiding this comment

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

Are you normalizing raw user input?

Copy link
Author

@Melonps Melonps Nov 25, 2025

Choose a reason for hiding this comment

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

@yurishkuro
yes,

Where does the frontend get the short id from in the first place?

Agree that, but I am specifically targeting the scenario where a user manually inputs a Trace ID into the search bar

Sometimes users might input a short ID (e.g., due to a copy paste error or typing only a part of it). Currently, sending this short ID to the backend causes a mismatch with the IDL, leading to an unhandled error that results in the infinite loading state. (UI should show Trace not found.

I agree that we shouldn't handle short IDs internally, but to fix this UX issue (the infinite spinner), I propose applying zero-padding specifically for the search bar input. This ensures the ID format matches what the backend expects (16 or 32 characters) and prevents the UI from hanging.

Copy link
Author

@Melonps Melonps Nov 25, 2025

Choose a reason for hiding this comment

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

Actually, It is likely that user input 31-char trace ID in input, because added 0 to tracd Id's head from backend response (ref: # #3079)

image

Copy link
Member

Choose a reason for hiding this comment

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

if a user inputs invalid ID we should display an error. That the UI keeps spinning in this case is a bug that we need to fix. But we do not need to fix invalid IDs. How do you know their copy&paste omitted the leading zero and not a trailing character?

Copy link
Author

Choose a reason for hiding this comment

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

That makes sense.
I will rework the change to implement client-side validation on the input form. If the user enters a Trace ID with an invalid length, an error will be shown immediately, which will effectively resolve the spinning UI bug while maintaining data integrity.

I'm still learning the ropes, so I really appreciate your feedback. I'll push the fixes shortly.

@Melonps
Copy link
Author

Melonps commented Nov 26, 2025

@yurishkuro

I’m planning to validate the user input as follows.
When the user enters an invalid trace ID, the TraceHeader component will display a validation error in red:
image

Also considering another approach using packages/jaeger-ui/src/utils/ValidatedFormField.tsx, which would show the validation error in a popover(but, two popovers appearing at the same time)

image image

@Melonps
Copy link
Author

Melonps commented Nov 30, 2025

@yurishkuro
Hi!, I update this PR, introducing validation function for checking traceId's digit into redux action and pass only valid IDs to fetch.
Along with it, I add ValidateError as type.


import { ValidateError } from '../types/validate-error';

export function validateTraceId(traceId: string): ValidateError | null {
Copy link
Member

Choose a reason for hiding this comment

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

The UI has no direct dependency on the trace ID being in any specific format. It's just an opaque string for the UI, it should not be adding any additional semantics to that string.

ids => ({ ids })
);

export const setTraceValidationError = createAction(
Copy link
Member

Choose a reason for hiding this comment

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

why does this need to be at the API level instead of just handled in TraceDiff?

traces[id] = { error, id, state: fetchedState.ERROR };
});
return { ...state, traces };
}
Copy link
Member

Choose a reason for hiding this comment

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

there is no packages/jaeger-ui/src/reducers/trace.js file in main branch. You fork might be stale.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: UI Shows Infinite Loading for Non-Existent Trace ID

2 participants