Repository: promptfoo/promptfoo
Issue: Trace visualizations inaccessible
Root cause: Architectural Constraint

**Describe the bug**
There are two interrelated problems:

  1. Circular Logic Dependency in Trace Tab Visibility

  File: EvalOutputPromptDialog.tsx
  Issue: "Traces" tab only appears if hasTraces is true, but hasTraces is only set by TraceView, which only renders when "Traces" tab is active.

  // Current problematic logic:
  const hasTracesData = Boolean(hasTraces && evaluationId);
  
  // Workaround I used - show tab if evaluation exists, let TraceView determine content:
  const hasTracesData = Boolean(evaluationId);

  2. testCaseId Format Mismatch

  File: EvalOutputPromptDialog.tsx
  Issue: Evaluation results use UUIDs for testCaseId, but trace system uses "testIdx-promptIdx" format.

  // Current problematic logic:
  // Filter traces by test case ID if provided
  const filteredTraces = testCaseId
    ? traces.filter((trace) => trace.testCaseId === testCaseId)
    : traces;

(Example workaround provided below)

**To Reproduce**
Steps to reproduce the behavior, including example Promptfoo configurations if applicable:

1. Send OpenTelemetry traces to Promptfoo and confirm their receipt (e.g. via logs, checking the DB, etc.)
2. Click on a test case magnifying glass icon

**Expected behavior**
The "Traces" tab is exposed, and traces are visualized 

** Actual behavior**
The "Traces" tab is not exposed, and traces are not visualized

**Screenshots**
If applicable, add screenshots to help explain your problem.

**System information:**

If possible, please output the results of `promptfoo debug` and paste the output here.

- Promptfoo version: HEAD

**Additional context**

  🔧 Patch 1: src/app/src/pages/eval/components/EvalOutputPromptDialog.tsx
```
    // Only show traces tab if we have actual traces to display
  - const hasTracesData = Boolean(hasTraces && evaluationId);
  + // Show traces tab if we have an evaluation ID, but let TraceView control final visibility
  + // This resolves the chicken-and-egg problem where TraceView needs to render to determine if traces exist
  + const hasTracesData = Boolean(evaluationId && (hasTraces || showTraceSection));

   interface EvalOutputPromptDialogProps {
     open: boolean;
     onClose: () => void;
     prompt: string;
     provider?: string;
     output?: string;
     gradingResults?: GradingResult[];
     metadata?: Record<string, any>;
     evaluationId?: string;
     testCaseId?: string;
     testIndex?: number;
  +  promptIndex?: number;
     variables?: Record<string, any>;
   }

   export default function EvalOutputPromptDialog({
     open,
     onClose,
     prompt,
     provider,
     output,
     gradingResults,
     metadata,
     evaluationId,
     testCaseId,
     testIndex,
  +  promptIndex,
     variables,
   }: EvalOutputPromptDialogProps) {

                 <DebuggingPanel
                   evaluationId={evaluationId}
                   testCaseId={testCaseId}
                   testIndex={testIndex}
  +                promptIndex={promptIndex}
                   showTraceSection={showTraceSection}
```

  🔧 Patch 2: src/app/src/pages/eval/components/EvalOutputCell.tsx
```
                 testIndex={rowIndex}
  +              promptIndex={promptIndex}
                 variables={output.testCase?.vars}
```

  🔧 Patch 3: src/app/src/pages/eval/components/DebuggingPanel.tsx
```
   interface DebuggingPanelProps {
     evaluationId?: string;
     testCaseId?: string;
     testIndex?: number;
  +  promptIndex?: number;
     showTraceSection: boolean;
     onTraceSectionVisibilityChange: (visible: boolean) => void;
   }

   export function DebuggingPanel({
     evaluationId,
     testCaseId,
     testIndex,
  +  promptIndex,
     showTraceSection,
     onTraceSectionVisibilityChange,
   }: DebuggingPanelProps) {

               <TraceView
                 evaluationId={evaluationId}
                 testCaseId={testCaseId}
                 testIndex={testIndex}
  +              promptIndex={promptIndex}
                 onVisibilityChange={onTraceSectionVisibilityChange}
               />
```

  🔧 Patch 4: src/app/src/components/traces/TraceView.tsx
```
   interface TraceViewProps {
     evaluationId?: string;
     testCaseId?: string;
     testIndex?: number;
  +  promptIndex?: number;
     onVisibilityChange?: (shouldShow: boolean) => void;
   }

   export default function TraceView({
     evaluationId,
     testCaseId,
     testIndex,
  +  promptIndex,
     onVisibilityChange,
   }: TraceViewProps) {

  -  // Filter traces by test case ID if provided
  -  const filteredTraces = testCaseId
  -    ? traces.filter((trace) => trace.testCaseId === testCaseId)
  -    : traces;
  +  // Filter traces by test case ID - handle format compatibility
  +  let filteredTraces;
  +
  +  if (!testCaseId || traces.length === 0) {
  +    filteredTraces = [];
  +  } else {
  +    const isTestCaseIdComposed = /^\d+-\d+$/.test(testCaseId);
  +    const isTracesArrayTestCaseIdComposed = traces.every(trace =>
  +      trace.testCaseId && /^\d+-\d+$/.test(trace.testCaseId)
  +    );
  +
  +    if (isTestCaseIdComposed === isTracesArrayTestCaseIdComposed) {
  +      // Both formats match - direct comparison
  +      filteredTraces = traces.filter((trace) => trace.testCaseId === testCaseId);
  +    } else if (isTestCaseIdComposed && !isTracesArrayTestCaseIdComposed) {
  +      // testCaseId is composed but traces aren't - no match possible
  +      filteredTraces = [];
  +    } else {
  +      // !isTestCaseIdComposed && isTracesArrayTestCaseIdComposed
  +      // testCaseId is not composed, traces are - parse and compare indices
  +      filteredTraces = traces.filter((trace) => {
  +        const parts = trace.testCaseId?.split('-') || [];
  +        if (parts.length !== 2) return false;
  +
  +        const traceTestIndex = parseInt(parts[0], 10);
  +        const tracePromptIndex = parseInt(parts[1], 10);
  +
  +        return !isNaN(traceTestIndex) &&
  +               !isNaN(tracePromptIndex) &&
  +               traceTestIndex === testIndex &&
  +               tracePromptIndex === promptIndex;
  +      });
  +    }
  +  }

  -  if (filteredTraces.length === 0 && testCaseId) {
  +  if (filteredTraces.length === 0 && (testCaseId || testIndex !== undefined)) {
```


Hi @szrn,

Sorry for the bug and for not closing this sooner - this was fixed in **PR #5676** on September 20, 2025 (2 days after you reported it), released in version 0.118.7.

A circular dependency was making trace visualizations inaccessible. This has been resolved - trace rendering now works correctly and all trace features should be accessible.

Please pull the latest version and trace visualizations should work as expected. If you encounter any further issues with traces, feel free to comment or re-open.

See the [LangSmith Integration](https://www.promptfoo.dev/docs/integrations/langsmith) and [Web UI Guide](https://www.promptfoo.dev/docs/usage/web-ui) for more info on traces.

Thanks for the quick report!