luvv to helpDiscover the Best Free Online Tools
Topic 2 of 8

Debugging And Error Handling

Learn Debugging And Error Handling for free with explanations, exercises, and a quick test (for Data Visualization Engineer).

Published: December 28, 2025 | Updated: December 28, 2025

Why this matters

As a Data Visualization Engineer, you ship charts and dashboards that guide business decisions. Debugging and error handling keeps your visualizations accurate, responsive, and trustworthy. Typical tasks include:

  • Tracking down broken charts after a schema change (missing or renamed columns).
  • Fixing async data-loading issues and race conditions in browser-based visualizations.
  • Handling divide-by-zero, NaN, or Infinity values before plotting.
  • Explaining errors to non-technical stakeholders with clear messages.

Concept explained simply

Debugging is a structured way to find the cause of a problem. Error handling is how your code reacts when things go wrong—without crashing or misleading users.

Mental model

  • Observe: Reproduce the issue and capture the exact error or wrong output.
  • Isolate: Make the failing piece smaller until the issue is obvious.
  • Explain: Form a hypothesis and test it quickly.
  • Prevent: Add validation, logging, and tests to stop it from returning.

Core tools you should know

Python quick toolkit
  • Tracebacks and exceptions: ValueError, KeyError, TypeError, ZeroDivisionError.
  • Logging: the logging module with levels (INFO, WARNING, ERROR), and structured context.
  • Interactive debugging: print(), breakpoint(), pdb/ipdb, inspecting shapes and dtypes.
  • Data checks: df.columns, df.isna().sum(), len(series), np.isfinite().
  • Safe operations: try/except, raising with context, input validation.
JavaScript quick toolkit
  • Console and DevTools: console.log/info/warn/error, breakpoints, network panel.
  • Async handling: try/catch with async/await; .catch for promises; check response.ok.
  • Error objects: new Error(message), error.stack.
  • D3/DOM checks: d3.select(...).size(), null/undefined guards, data length checks.

Worked examples

Example 1 (Python): Plot fails with shape mismatch

Symptom: ValueError: x and y must have same first dimension.

import matplotlib.pyplot as plt
x = [1,2,3]
y = [1,2]
plt.plot(x, y)  # ValueError

Debug steps:

  • Print lengths to confirm mismatch.
  • Trace where y lost elements (filter, join, or missing data).
  • Align series before plotting.
print(len(x), len(y))  # 3, 2
# Fix: align/clean data
x = x[:2]
plt.plot(x, y)
plt.title("Aligned lengths; plot works")

Example 2 (Python): KeyError after data schema change

Symptom: KeyError: 'revenue' when grouping data.

import pandas as pd

df = pd.DataFrame({"date":["2024-01-01"], "gross_revenue":[100]})
# Code expects column 'revenue'
df.groupby("date", as_index=False)["revenue"].sum()  # KeyError

Debug steps:

  • Inspect columns: print(df.columns.tolist()).
  • Check for whitespace/typos: strip, lower.
  • Map old to new names and add validation.
print(df.columns.tolist())  # ['date', 'gross_revenue']
col_map = {"revenue": "gross_revenue"}
if "revenue" not in df.columns and "gross_revenue" in df.columns:
    df = df.rename(columns={"gross_revenue": "revenue"})
result = df.groupby("date", as_index=False)["revenue"].sum()
print(result)

Example 3 (JavaScript + D3): Empty chart due to bad selector and fetch errors

Symptom: No bars render. No obvious error.

// Suppose the container id is 'chart', but code uses '#charts'
async function render() {
  try {
    const res = await fetch('data.json');
    if (!res.ok) throw new Error(`HTTP ${res.status}`);
    const data = await res.json();

    const root = d3.select('#chart');
    if (root.size() === 0) throw new Error('Root container not found');
    if (!Array.isArray(data) || data.length === 0) throw new Error('No data');

    console.info(`Loaded ${data.length} items`);
    const w = 300, h = 150;
    const x = d3.scaleBand().domain(data.map((d,i) => i)).range([0, w]).padding(0.1);
    const y = d3.scaleLinear().domain([0, d3.max(data, d => d.value)]).range([h, 0]);

    const svg = root.append('svg').attr('width', w).attr('height', h);
    svg.selectAll('rect')
      .data(data)
      .join('rect')
      .attr('x', (d,i) => x(i))
      .attr('y', d => y(d.value))
      .attr('width', x.bandwidth())
      .attr('height', d => h - y(d.value))
      .attr('fill', '#4f46e5');
  } catch (e) {
    console.error('Render failed:', e.message);
  }
}
render();

Fixes applied:

  • Correct selector '#chart' and assert the root exists.
  • Check response.ok and handle HTTP errors.
  • Validate that data is an array and non-empty.

Debugging workflow — step-by-step

  1. Reproduce reliably

    Capture the exact inputs, environment, and steps to make it fail. If it fails only sometimes, log timestamps and inputs to spot patterns.

  2. Surface the error

    Enable verbose logs and show full tracebacks. In JS, check the Network and Console panels; in Python, print shapes/dtypes and top rows.

  3. Minimize the code

    Comment out unrelated parts. Replace live data with a tiny fixture that still fails. The smaller the failing case, the faster the fix.

  4. Hypothesize and test

    Form a simple cause theory. Test it with a quick change or a targeted print/breakpoint. Iterate until the error disappears for the right reason.

  5. Harden and prevent regressions

    Add input validation, try/catch with clear messages, and basic unit checks (length, NaN counts). Keep a short note in the code explaining the known pitfall.

Common mistakes and self-check

  • Silencing errors with bare except or empty catch blocks. Self-check: Do you log the original error and stack?
  • Assuming data is clean. Self-check: Do you assert non-negative values, non-zero denominators, and expected column names?
  • Ignoring HTTP status. Self-check: Do you check response.ok before parsing JSON?
  • Plotting before validating lengths. Self-check: Do you compare len(x) and len(y) and inspect NaN counts?
  • Swallowing async errors in JS promise chains. Self-check: Do you have a .catch or try/catch around awaits?

Exercises

Complete these hands-on exercises. A quick checklist is provided for each. Solutions are available under toggles.

Exercise 1 (Python): Robust CTR computation

Goal: Compute CTR = clicks / impressions per day from a DataFrame while handling missing columns, zeros, and NaNs without crashing.

  • Checklist:
    • Validate required columns or map legacy names.
    • Handle division by zero by returning 0 for those rows.
    • Log how many rows were dropped or fixed.
    • Return a DataFrame with date and ctr columns.

Exercise 2 (JavaScript): Safe fetch + D3 render

Goal: Load data from a JSON endpoint, validate it, and render a simple bar chart. Handle bad HTTP status, JSON parse errors, missing root node, and empty data arrays.

  • Checklist:
    • Check response.ok and throw on failure.
    • Wrap await in try/catch and log errors clearly.
    • Validate that data is an array of objects with a numeric value field.
    • Guard against missing root selection and empty data.

Mini challenge

Add lightweight telemetry to a data-to-chart function (Python or JS): log input size, number of invalid records dropped, render time in ms, and final mark count. Trigger a synthetic error (e.g., missing column or HTTP 404) to confirm that your error messages identify the exact stage and cause.

  • Acceptance criteria:
    • Includes start/end timestamps and a duration.
    • Counts and reports dropped/invalid items.
    • On error, shows stage name and the original error message.

Who this is for

  • Analytics and BI practitioners who script visualizations in Python or JavaScript.
  • Data Visualization Engineers moving from notebooks to production dashboards.

Prerequisites

  • Basic Python (lists, dicts, functions) and/or JavaScript (ES6, modules, async/await).
  • Familiarity with Pandas/Matplotlib or D3/DOM basics.

Learning path

  1. Practice reading tracebacks and console stacks.
  2. Add input validation and logging to existing charts.
  3. Introduce structured error handling (try/except or try/catch) around data loading and transformation.
  4. Refactor into small testable steps with clear contracts and assertions.

Practical projects

  • Build a resilient time-series line chart that auto-detects and annotates gaps (NaN runs) and logs all imputations.
  • Create a small dashboard that loads two datasets concurrently; handle partial failures with fallback messages.
  • Write a "data validator" utility that checks column presence, types, ranges, and returns a report before plotting.

Next steps

  • Instrument one of your existing visualizations with validations, guards, and clear error messages.
  • Run through the quick test below to confirm understanding. Note: The Quick Test is available to everyone; only logged-in users get saved progress.

Practice Exercises

2 exercises to complete

Instructions

Write a function compute_daily_ctr(df) that returns a DataFrame with columns [date, ctr]. Requirements:

  • Accept column names clicks and impressions (or legacy clicks_total and impressions_total).
  • Handle division by zero by setting ctr to 0 where impressions == 0.
  • Drop rows where clicks or impressions is missing; log how many were dropped.
  • Round ctr to 4 decimals.
import pandas as pd

def compute_daily_ctr(df: pd.DataFrame) -> pd.DataFrame:
    ...  # your code

# Test input
raw = pd.DataFrame({
  'date': ['2024-01-01','2024-01-01','2024-01-02','2024-01-02'],
  'clicks_total': [10, None, 5, 2],
  'impressions_total': [100, 0, 0, 20]
})
print(compute_daily_ctr(raw))
Expected Output
A DataFrame like: date, ctr -> 2024-01-01, 0.1000 and 2024-01-02, 0.1000. Console logs the number of dropped rows and zeros handled.

Debugging And Error Handling — Quick Test

Test your knowledge with 8 questions. Pass with 70% or higher.

8 questions70% to pass

Have questions about Debugging And Error Handling?

AI Assistant

Ask questions about this tool