Debugging challengeIntermediate

The config that lied at the boundary

Scenario

A crawler loads its settings from a JSON file: a maximum crawl depth and a per-page-type score weight table. The loader compiles clean under strict: true and every type reads correctly in the IDE. In production the crawler dies two ways: scorePage throws TypeError: Cannot read properties of undefined (reading 'article') deep in the scoring pass, and on another config file the crawl recurses until RangeError: Maximum call stack size exceeded. Neither stack trace mentions the config loader. Find the real bug and say why both crashes surface so far from it.

1interface CrawlConfig {
2 maxDepth: number;
3 weights: Record<string, number>; // score per page type
4}
5
6function loadConfig(text: string): CrawlConfig {
7 return JSON.parse(text) as CrawlConfig; // "as" — trust me, compiler
8}
9
10interface Page { type: string; links: Page[] }
11
12function scorePage(page: Page, config: CrawlConfig): number {
13 return config.weights[page.type] ?? 0; // throws when weights is undefined
14}
15
16function crawl(page: Page, depth: number, config: CrawlConfig, out: string[]): void {
17 if (depth === config.maxDepth) return; // never true when maxDepth is "3"
18 out.push(page.type);
19 for (const child of page.links) crawl(child, depth + 1, config, out);
20}
21
22// The file was hand-edited: maxDepth quoted, weights misspelled.
23const config = loadConfig('{"maxDepth": "3", "weigths": {"article": 5, "index": 1}}');
24
25const home: Page = { type: 'index', links: [] };
26const article: Page = { type: 'article', links: [home] };
27home.links.push(article); // real sites have cycles
28
29console.log(scorePage(article, config)); // TypeError: reading 'article' of undefined
30const visited: string[] = [];
31crawl(home, 0, config, visited); // RangeError: Maximum call stack size exceeded

Your task

  1. What is the return type of JSON.parse, and what does as CrawlConfig check at compile time and do at runtime?
  2. Explain the TypeError in scorePage: the code even has ?? 0 — why does the guard not help?
  3. Explain the RangeError: depth === config.maxDepth with maxDepth holding the string "3". Would depth >= config.maxDepth have "worked", and why is that worse?
  4. Rewrite the loader so a bad file fails *at the boundary* with a message naming the problem: parse into unknown and narrow with a type guard.
  5. Where else does this pattern appear besides JSON.parse? Name two more trust boundaries and the general rule.
  6. State the cost of validation relative to the crawl itself.
DebuggingEdge CasesSystematic Reasoning

Work it out

Write your analysis before revealing anything. The self-check below compares it against what a strong answer contains.

Reveal

Progressive — each section builds on the previous one.

The bug
Why it happens
The fix
Edge cases
Complexity

Self-check

Tick what your analysis covered. Be honest — this feeds your readiness profile.

0/6

Related concepts