Customization Troubleshooting
Use the live customization state first. It shows what the SDK accepted and what will be saved in chart state.

First diagnostic
- Read
getCustomizationState(). - Inspect the rendered/provenance snapshot for the exact field.
- Identify the highest active precedence layer.
- Change the owning layer once.
- Verify both the live surface and a save/reload round trip.
const state = chart.customization().getCustomizationState();
console.log(state);
My override is stored but not visible
First inspection: chart.customization().trackSnapshots(...).inspect().fieldApplication — it classifies each field as applied, conditional, application-owned, stored-only, or unknown.
Expected value: the field reports applied. A stored-only or conditional result explains the symptom by itself.
Whether a group has a live chart effect depends on the bucket:
| Bucket | Live effect |
|---|---|
chartOverrides.mainSeries, grid, axes, sessions | Applied directly to the chart |
chartOverrides.chart, panes, scales | Applied through their typed native fields: chart.background, panes.separator, scales.axis, scales.xAxis, scales.yAxis |
chartOverrides.trading | Applied by trading overlays for orders, positions, and executions |
styleOverrides.styles, background, crosshair, grid, separator, typed indicators, typed drawings, handles, typed candles, typed text, typed colors.grid | Applied directly to the chart |
styleOverrides.averageClosePriceLine, bidAskPriceLine, highLowPriceLine, previousClosePriceLine | Applied by reference price-line overlays when the matching display setting and market data make the line visible |
styleOverrides.toolbar, styleOverrides.modals | Stored/reserved buckets — live toolbar and modal appearance uses ChartTheme.ui |
| Symptom | First check | Expected | Owner | Fix | Verify |
|---|---|---|---|---|---|
Value appears in getCustomizationState() but not on the chart | trackSnapshots(...).inspect().fieldApplication | Field classified applied | SDK override contract | Move the setting to an applied bucket | Field reports applied and the chart repaints |
| A stored native value has the wrong shape | validateCustomizationState(...) or inspect().diagnostics | Empty diagnostics | Native customization contract | Replace the value with the typed native shape before saving or replaying state | Diagnostics come back empty on the next inspect |
Toolbar or modal colors ignore applyStyleOverrides(...) | getCustomizationState().styleOverrides.toolbar / .modals | Nothing relies on these stored buckets | Theme UI tokens | Use ChartTheme.ui.button, ChartTheme.ui.modal, or scoped CSS | Toolbar/modal recolors after setTheme |
| Trading line colors do not update | Orders, positions, or executions attached to the chart | Overlay data present | Trading overlay state | Apply chartOverrides.trading and update the trading overlay data source | Lines restyle once overlay data exists |
My candle colors do not round trip
First inspection: chart.customization().getCandleColors().
Expected value: exactly the colors you set.
Correction: use setCandleColors(...) for common candle colors instead of hand-built nested patches:
chart.customization().setCandleColors({
up: '#16a34a',
down: '#dc2626',
noChange: '#94a3b8',
});
const colors = chart.customization().getCandleColors();
Verification: getCandleColors() returns the values you set, and they survive a getState() / setState() round trip. Low-level candle overrides use the nested candles.bar shape documented by StyleOverrideSettings.
My theme changed but some series colors did not
First inspection: getCustomizationState().styleOverrides.candles and getCandleColors().
Expected value: empty when the theme should own series colors. ChartTheme is the base skin; explicit style overrides are higher precedence, so any value here pins the color across theme switches.
Correction: update or clear those overrides when switching themes.
Verification: switch the theme again — the candle colors now follow the preset.
My restored layout uses the old theme
First inspection: chart.customization().getTheme() after applyLayout(...).
Expected value: the host's product theme — but layouts include state.customization, so a saved layout that carries a theme restores that theme.
Correction: reapply the host theme after applyLayout(...) when the product theme should always win:
await chart.applyLayout(savedLayout);
chart.customization().setTheme(hostTheme);
Verification: getTheme() returns the host theme and the chart repaints with it. This is the same precedence rule used by setState(...): restored customization is state, and a later runtime customization call wins again.
My user setting overrides a default
First inspection: compare the stored UserSettingsState with getCustomizationState() — they are separate surfaces.
Expected value: loading UserSettingsState never rewrites ChartOverrideSettings, StyleOverrideSettings, or ChartTheme by itself; host code has to choose to apply a saved preference to customization.
Correction: use features.userSettings and ChartStorageAdapter for product preferences such as favorite intervals or host-defined settings. Use initialState.customization and chart.customization() for chart appearance.
Verification: reload with the user setting present — customization state is unchanged until your code applies it.
My CSS is missing in the host application
First inspection: the host bundle's imports.
Expected value: the published CSS entry point imported once:
import '@tradescript/pro/style.css';
Correction: add the import. If the host uses the compiled utility layer directly, also import @tradescript/pro/tailwind.css. For CDN or script-tag deployment, link the built CSS files alongside the UMD bundle.
Verification: chart chrome renders styled after rebuilding the application, including in a clean production installation.
My custom indicator or embed fails under CSP
First inspection: the browser console, for blocked worker-src, style-src, or frame-src entries. SDK worker-backed compute paths, bundled styles, and embedded media can all be blocked by a restrictive Content Security Policy. Trusted custom indicator modules are bundled with the host; the public SDK does not compile customer indicator source at runtime.
Expected value: no CSP violations naming SDK workers or styles.
Correction: disable the affected feature in features, provide worker clients or factories with main-thread fallback diagnostics, or update CSP to allow the exact trusted worker origin. Do not relax CSP broadly just to make chart customization work.
Verification: the console shows no CSP violations and the feature loads.
I need to know what changed
Subscribe to customization events:
const unsubscribe = chart.customization().subscribe((event) => {
if (event.type === 'overrides-change') {
console.log(event.state.chartOverrides, event.state.styleOverrides);
}
});
Widget-level event handlers also receive customization-change events.
For field-level provenance, track native snapshots:
const tracker = chart.customization().trackSnapshots('constructor');
chart.customization().applyStyleOverrides({
grid: { horizontal: { color: '#1f2937' } },
});
const inspection = tracker.inspect();
console.table(inspection.effectiveFields);
tracker.dispose();
effectiveFields shows the current field value and the source event that last changed it. fieldHistory includes cleared fields, so it can explain why a value that used to exist is no longer effective. fieldApplication separates applied, conditional, host-owned, state-only, and unknown fields. diagnostics reports invalid typed native customization values in the final snapshot, including the source event when the bad value came from tracked snapshots.
Related pages
- Customization Precedence — the layer order behind most of these symptoms.
- Overrides — which buckets apply directly and which are stored only.
- Troubleshooting — the wider symptom-led diagnostic sequences.