tscircuit / tscircuit/core

Empty string in `connections` crashes the render with a raw css-what parser error

Open
#2,865 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
TypeScript
Stars
58
Forks
203
Avg merge
7h 39m
Merged PRs (30d)
286

Description

Summary

An empty string in connections aborts the whole render with a raw CSS-parser error that names neither the component nor the pin.

<board width="30mm" height="20mm" routingDisabled>
  <resistor name="R1" resistance="1k" footprint="0402" pcbX={-8} />
  <capacitor
    name="C1"
    capacitance="1uF"
    footprint="0402"
    connections={{ pin1: "", pin2: ".R1 > .pin1" }}
  />
</board>

Actual — renderUntilSettled() throws, and no circuit JSON is produced at all:

error: Expected name, found .
  at getName (node_modules/css-what/lib/commonjs/parse.js:95:23)
  at parseSelector (node_modules/css-what/lib/commonjs/parse.js:220:17)
  at selectOne (lib/components/base-components/PrimitiveComponent/PrimitiveComponent.ts:1253:18)
  at resolveImplicitSinglePort (lib/components/primitive-components/Trace/Trace__findConnectedPorts.ts:29:47)

Two separate problems:

  1. The message is not actionable. Expected name, found . is css-what internals. It doesn't say which component, which pin, or that the value was empty. Reading it, the natural guess is that .R1 > .pin1 — the one target that is actually fine — is malformed.
  2. It takes the whole board down. This is a thrown exception, not a recorded error, so the sibling valid connection, the other components, and every downstream phase never run. One typo in one prop produces zero output.
Cause

NormalComponent._createTracesFromConnectionsProp() forwards every entry of props.connections straight into a Trace's to:

for (const [pinName, target] of Object.entries(props.connections)) {
  const targets = Array.isArray(target) ? target : [target]
  for (const targetPath of targets) {
    this.add(new Trace({ from: `.${this.name} > .${pinName}`, to: targetPath }))
  }
}

An empty string isn't a selector, but nothing rejects it here, so to: "" reaches selectOne and css-what throws while parsing. The parser error surfaces from a call stack several layers below the prop the user actually wrote.

Note the value survives validation upstream too — connections is typed as a record of selector strings, and "" is a string, so the props schema accepts it.

Expected

An empty or whitespace-only target should be reported as a misconfiguration that names the component and the pin, and the rest of the board should still render — matching how other bad-input cases in the codebase behave (source_component_misconfigured_error, source_ambiguous_port_reference), which record an error and continue rather than throwing.

Suggested behaviour:

<capacitor#13 name=".C1" /> has an empty connections target for pin "pin1".
Provide a selector such as ".R1 > .pin1" or "net.VCC", or remove the entry.

…with the sibling pin2: ".R1 > .pin1" connection still producing its source_trace.

Scope / judgement calls left open
  • I only treat empty and whitespace-only targets as misconfigured. A non-empty but unresolvable selector (e.g. ".R9 > .pin1" where R9 doesn't exist) already has its own downstream handling, so I deliberately left that path untouched.
  • The check sits in _createTracesFromConnectionsProp so it covers both the string and array forms of a connections entry.
  • Rejecting "" at the props-schema level in tscircuit/props would catch it earlier, but that's a cross-repo change with a wider blast radius (it would turn today's runtime crash into a hard parse failure for anyone who currently passes "" deliberately). I kept the fix in core; happy to follow up in props if maintainers prefer that.

PR follows.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in lib/components/primitive-components/NormalComponent/NormalComponent.ts at _createTracesFromConnectionsProp, then read the Trace path through PrimitiveComponent.ts and Trace__findConnectedPorts.ts. Verify the empty and whitespace-only cases against the existing misconfiguration error behavior; done means rendering records a component-and-pin error, continues, and still produces the valid sibling source_trace.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
tooling
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
76/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.