Skip to content

fix: make display_chart series color optional to prevent chart loading failure#458

Open
leoncuhk wants to merge 1 commit intogetnao:mainfrom
leoncuhk:fix/display-chart-color-optional
Open

fix: make display_chart series color optional to prevent chart loading failure#458
leoncuhk wants to merge 1 commit intogetnao:mainfrom
leoncuhk:fix/display-chart-color-optional

Conversation

@leoncuhk
Copy link

Problem

LLMs frequently omit the color field when calling the display_chart tool — the series array is
generated as [{"data_key": "...", "label": "..."}] without a color property.

Since SeriesConfigSchema.color is currently required, Zod validation rejects the input with:

Invalid input for tool display_chart: Type validation failed
"series", 0, "color" — "expected": "string", "received": "undefined"

The chart component gets stuck in "Loading chart" state permanently because the tool state never
transitions from input-streaming to result.

Root Cause

The color field description says "defaults to theme colors" but the schema doesn't actually provide
a default — it's a required string. The frontend already has fallback logic (s.color || Colors[idx % Colors.length]) but it's never reached because validation fails first.

Fix

  • apps/shared/src/tools/display-chart.ts: Make color optional with
    .optional().default('auto')
  • apps/frontend/src/components/tool-calls/display-chart.tsx: Handle 'auto' sentinel value,
    falling back to theme colors (2 locations: chart config + legend payload)

Author: Leon leoncuhk@gmail.com

…g failure

LLMs frequently omit the `color` field in `display_chart` tool calls,
causing Zod validation to reject the input with "Required" error.
The chart gets stuck in "Loading chart" state permanently.

- Make `SeriesConfigSchema.color` optional with default `'auto'`
- Frontend already had fallback logic (`s.color || Colors[...]`) but
  it was never reached because validation failed first
- Handle `'auto'` sentinel in frontend color resolution

Author: Leon <leoncuhk@gmail.com>
Copilot AI review requested due to automatic review settings March 16, 2026 13:49
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR fixes display_chart tool-call validation failures when LLMs omit series[].color, preventing the UI from getting stuck in a perpetual “Loading chart” state by allowing a theme-based fallback color to be used.

Changes:

  • Update the shared Zod schema to accept missing series[].color by making it optional and defaulting it to 'auto'.
  • Update the frontend chart rendering to treat 'auto' as “use theme colors” (for both chart config colors and legend payload colors).

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.

File Description
apps/shared/src/tools/display-chart.ts Makes SeriesConfigSchema.color optional and defaults it to 'auto' to avoid tool input validation failures.
apps/frontend/src/components/tool-calls/display-chart.tsx Adds explicit handling of the 'auto' sentinel so the chart/legend fall back to theme colors.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

export const SeriesConfigSchema = z.object({
data_key: z.string().describe('Column name from SQL result to plot.'),
color: z.string().describe('CSS color (defaults to theme colors).'),
color: z.string().describe('CSS color (defaults to theme colors).').optional().default('auto'),
export const SeriesConfigSchema = z.object({
data_key: z.string().describe('Column name from SQL result to plot.'),
color: z.string().describe('CSS color (defaults to theme colors).'),
color: z.string().describe('CSS color (defaults to theme colors).').optional().default('auto'),
Comment on lines 268 to 270
label: s.label || labelize(s.data_key),
color: s.color || Colors[idx % Colors.length],
color: (s.color && s.color !== 'auto') ? s.color : Colors[idx % Colors.length],
};
Comment on lines 286 to 289
value: s.label || labelize(s.data_key),
dataKey: s.data_key,
color: s.color || Colors[idx % Colors.length],
color: (s.color && s.color !== 'auto') ? s.color : Colors[idx % Colors.length],
isHidden: hiddenSeriesKeys.has(s.data_key),
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

No issues found across 2 files


Since this is your first cubic review, here's how it works:

  • cubic automatically reviews your code and comments on bugs and improvements
  • Teach cubic by replying to its comments. cubic learns from your replies and gets better over time
  • Add one-off context when rerunning by tagging @cubic-dev-ai with guidance or docs links (including llms.txt)
  • Ask questions if you need clarification on any suggestion

@github-actions
Copy link
Contributor

🚀 Preview Deployment

URL https://pr-458-c74b200.preview.getnao.io
Commit c74b200

⚠️ No LLM API keys configured - you'll see the API key setup flow when trying to chat.


Preview will be automatically removed when this PR is closed.

Copy link
Contributor

@Bl3f Bl3f left a comment

Choose a reason for hiding this comment

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

Is there really an issue to fix here? I feel that eventually we could make it .optional() but with || it fixes already the issue if not defined, we don't need to pass 'auto' etc.

(and js lint is failing)

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.

3 participants