Skip to content

console.table: skip Symbol entries in the properties filter - #36150

Closed
robobun wants to merge 4 commits into
mainfrom
farm/a962775b/console-table-symbol-properties
Closed

robobun wants to merge 4 commits into
mainfrom
farm/a962775b/console-table-symbol-properties

Conversation

@robobun

@robobun robobun commented Jul 27, 2026 •

Copy link
Copy Markdown
Collaborator

What

console.table(data, properties) and Bun.inspect.table(data, properties) throw TypeError: Cannot convert a symbol to a string when the properties array contains a Symbol. Node prints the table and drops the Symbol column.

const data = [{ a: 1, b: 2 }];
console.table(data, [Symbol('col'), 'a']);
// node: prints table with just column 'a'
// bun:  TypeError: Cannot convert a symbol to a string

Bun's own new console.Console(stream).table(...) (the JS port) already has Node parity here; only the native TablePrinter path throws.

Cause

TablePrinter::print_table pre-populates columns by iterating the properties array and calling to_bun_string (ToString) on every entry. ToString on a Symbol throws per spec.

Node keys a null-proto map object by each properties entry and later reads the column set back via ObjectKeys(map), which never enumerates symbol keys, so a Symbol entry is silently dropped. It never ToStrings the entry.

Fix

Skip is_symbol() entries in the properties iterator before coercing. Non-Symbol primitives (numbers, plain objects) still ToString into a column header, which matches Node.

Verification

New console.table with a Symbol in the properties filter block in test/js/bun/console/console-table.test.ts covering both Bun.inspect.table and a spawned console.table, with a non-Symbol primitive control. Both new tests throw on USE_SYSTEM_BUN=1; all 35 existing tests in the file (plus the 35 in bun-inspect-table.test.ts) continue to pass under bun bd.

Related: #34241 addresses Symbol keys on the data objects but still ToStrings properties entries, so it does not fix this throw.


[stamp-90s] gate passed · iteration 1 · 2 files touched

fails on main (without fix)
ASAN without fix: 2 failed, 1 skipped
$ BUN_DEBUG_QUIET_LOGS=1 bun scripts/build.ts --profile=debug --quiet test "--reporter=junit" "--reporter-outfile=/tmp/mechgate.xml" test/js/bun/console/console-table.test.ts
bun test v1.4.0 (43ef67de4)

test/js/bun/console/console-table.test.ts:
┌───┐
│   │
├───┤
└───┘
┌───┐
│   │
├───┤
└───┘
(pass) console.table > throws when second arg is invalid [7.23ms]
(pass) console.table > expected output for: not object (number) [10.30ms]
(pass) console.table > expected output for: not object (string) [3.92ms]
(pass) console.table > expected output for: object - empty [1.61ms]
(pass) console.table > expected output for: object [2.00ms]
(pass) console.table > expected output for: array - empty [1.74ms]
(pass) console.table > expected output for: array - plain [1.54ms]
(pass) console.table > expected output for: array - object [2.04ms]
(pass) console.table > expected output for: array - objects with diff props [1.81ms]
(pass) console.table > expected output for: array - mixed [2.45ms]
(pass) console.table > expected output for: set [1.91ms]
(pass) console.table > expected output for: map [2.16ms]
(pass) conso
... (truncated)

release without fix: 1 skipped
bun test v1.4.0-canary.1 (9db7a0920)

test/js/bun/console/console-table.test.ts:
┌───┐
│   │
├───┤
└───┘
┌───┐
│   │
├───┤
└───┘
(pass) console.table > throws when second arg is invalid [0.11ms]
(pass) console.table > expected output for: not object (number) [0.42ms]
(pass) console.table > expected output for: not object (string) [0.03ms]
(pass) console.table > expected output for: object - empty [0.81ms]
(pass) console.table > expected output for: object [0.07ms]
(pass) console.table > expected output for: array - empty [0.01ms]
(pass) console.table > expected output for: array - plain [0.01ms]
(pass) console.table > expected output for: array - object [0.02ms]
(pass) console.table > expected output for: array - objects with diff props [0.02ms]
(pass) console.table > expected output for: array - mixed [0.01ms]
(pass) console.table > expected output for: set [0.02ms]
(pass) console.table > expected output for: map [0.05ms]
(pass) console.table > expected output for: properties [0.02ms]
(pass) console.table > expected output for: properties - empty [0.01ms]
(pass) console.table > expected output for:
... (truncated)
passes on PR (with fix)
ASAN with fix: 1 skipped
$ BUN_DEBUG_QUIET_LOGS=1 bun scripts/build.ts --profile=debug --quiet test "--reporter=junit" "--reporter-outfile=/tmp/mechgate.xml" test/js/bun/console/console-table.test.ts
bun test v1.4.0 (43ef67de4)

test/js/bun/console/console-table.test.ts:
┌───┐
│   │
├───┤
└───┘
┌───┐
│   │
├───┤
└───┘
(pass) console.table > throws when second arg is invalid [8.67ms]
(pass) console.table > expected output for: not object (number) [11.48ms]
(pass) console.table > expected output for: not object (string) [2.13ms]
(pass) console.table > expected output for: object - empty [1.68ms]
(pass) console.table > expected output for: object [2.05ms]
(pass) console.table > expected output for: array - empty [1.35ms]
(pass) console.table > expected output for: array - plain [2.07ms]
(pass) console.table > expected output for: array - object [2.09ms]
(pass) console.table > expected output for: array - objects with diff props [1.84ms]
(pass) console.table > expected output for: array - mixed [2.41ms]
(pass) console.table > expected output for: set [2.00ms]
(pass) console.table > expected output for: map [2.24ms]
(pass) conso
... (truncated)

release with fix: 1 skipped
$ bun scripts/build.ts --profile=release
[configured] bun-profile → bun (stripped) in 752ms (unchanged)
ninja: Entering directory `/workspace/bun/build/release'
[1/5] gen generated_host_exports.rs
generated_host_exports.rs: 94 exports (host=3, lazy=10, generic=81, rust=0); 240 extern-C blocks audited
[1/5] cargo bun_bin → libbun_rust.a (--target x86_64-unknown-linux-gnu)

  nightly-2026-07-20-x86_64-unknown-linux-gnu unchanged - rustc 1.99.0-nightly (9f36de775 2026-07-19)

�[1m�[92m   Compiling�[0m bun_jsc v0.0.0 (/workspace/bun/src/jsc)
�[1m�[92m   Compiling�[0m bun_js_parser_jsc v0.0.0 (/workspace/bun/src/js_parser_jsc)
�[1m�[92m   Compiling�[0m bun_ast_jsc v0.0.0 (/workspace/bun/src/ast_jsc)
�[1m�[92m   Compiling�[0m bun_sourcemap_jsc v0.0.0 (/workspace/bun/src/sourcemap_jsc)
�[1m�[92m   Compiling�[0m bun_bundler_jsc v0.0.0 (/workspace/bun/src/bundler_jsc)
�[1m�[92m   Compiling�[0m bun_patch_jsc v0.0.0 (/workspace/bun/src/patch_jsc)
�[1m�[92m   Compiling�[0m bun_http_jsc v0.0.0 (/workspace/bun/src/http_jsc)
�[1m�[92m   Compiling�[0m bun_semver_jsc v0.0.0 (/workspace/bun/src/semver_jsc)
�[1m�[92m   Compiling�[0m bun_sys_jsc v0.0.0 (/workspace/bun/src/sys_jsc)

... (truncated)
diff hotspot
src/jsc/ConsoleObject.rs                  |  4 ++++
 test/js/bun/console/console-table.test.ts | 26 ++++++++++++++++++++++++++
 2 files changed, 30 insertions(+)

gate history · 1 passed · 1 rejected · iteration 1

evidence per changed file
file                                       reads  edits  tests
src/jsc/ConsoleObject.rs                       2      2      0
test/js/bun/console/console-table.test.ts      2      1      0

Node keys a null-proto object by each properties entry and reads the
column set back via ObjectKeys, so a Symbol entry is silently dropped.
Bun's native TablePrinter called ToString on every entry instead, and
ToString on a Symbol throws, so console.table(data, [Symbol(), 'a'])
and Bun.inspect.table(data, [Symbol(), 'a']) both threw a TypeError
instead of printing the table.

Skip Symbol entries when pre-populating columns from the properties
array. Non-Symbol primitives still ToString into a column header
(matching Node).
@robobun

robobun commented Jul 27, 2026 •

Copy link
Copy Markdown
Collaborator Author
Updated 4:44 PM PT - Jul 27th, 2026

❌ @robobun, your commit 43ef67d has 1 failures in Build #83642 (All Failures):


🧪   To try this PR locally:

bunx bun-pr 36150

That installs a local version of the PR into your bun-36150 executable, so you can run:

bun-36150 --bun

Comment thread src/jsc/ConsoleObject.rs Resolved
@coderabbitai

coderabbitai Bot commented Jul 27, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

Changes

The table printer now ignores Symbol entries in the properties filter. Tests verify Symbol columns are omitted from Bun.inspect.table and console.table output while other properties remain rendered.

Console table Symbol filtering

Layer / File(s) Summary
Filter Symbol properties and validate output
src/jsc/ConsoleObject.rs, test/js/bun/console/console-table.test.ts
print_table skips Symbol properties during column generation, with coverage for inspection and subprocess console output.

Suggested reviewers: jarred-sumner

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title is concise and accurately summarizes the main change to console.table property handling.
Description check ✅ Passed The description covers what changed and how it was verified, with enough detail despite using non-template headings.

Comment @coderabbitai help to get the list of available commands.

Comment thread src/jsc/ConsoleObject.rs Resolved

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM. The boxed-Symbol edge case I raised earlier was resolved with a solid rationale (no JSType::SymbolObject in JSC, no existing Rust binding for the check) and is not worth a new C++ export here.

What was reviewed:

  • The is_symbol() guard sits directly before the only to_bun_string coercion in the properties-filter path — no sibling sites in TablePrinter need the same treatment.
  • Tests cover both entry points (Bun.inspect.table in-process and spawned console.table), include a non-Symbol-primitive control, and follow harness conventions (concurrent pipe drain, combined-object assert).
Extended reasoning...

Overview

Three-line guard in src/jsc/ConsoleObject.rs (TablePrinter::print_table) that skips Symbol entries when iterating the properties filter array, plus a 26-line test block in test/js/bun/console/console-table.test.ts. Without the guard, to_bun_string (spec ToString) throws on a Symbol; Node silently drops Symbol columns because it keys a map via ToPropertyKey and reads columns back via ObjectKeys.

Security risks

None. This is display-only formatting logic in console.table. The change narrows the set of inputs that reach ToString; it does not add any new coercion, allocation, or side-effect path.

Level of scrutiny

Low. A tiny, self-contained Node-compat fix in a non-critical output-formatting path. The guard is placed immediately before the single throwing call site; grep confirms this is the only to_bun_string(global_object) in the properties-iteration branch. Error propagation via ? is unchanged for every other entry type.

Other factors

  • The comment-cop bot's paragraph-comment complaint was addressed in 9db7a09 (trimmed to one line); thread resolved.
  • My prior nit about Object(Symbol()) was answered with concrete evidence: JSC assigns SymbolObject plain ObjectType (no dedicated JSType to test), no toPropertyKey Rust binding exists, and Node itself throws on the adjacent @@toPrimitive → Symbol case. Covering the wrapper would require a new C++ export for a case nobody writes; I explicitly marked it non-blocking and the thread is resolved.
  • Tests follow REVIEW.md conventions: await using for the spawned process, Promise.all on stdout/stderr/exited, combined-object assertion, bunEnv/bunExe, exact-output .toBe (no snapshot drift), and a control case proving non-Symbol primitives still coerce. PR description confirms USE_SYSTEM_BUN=1 fails and bun bd passes.
  • No bugs found by the bug-hunting system this run.

@robobun

robobun commented Jul 27, 2026

Copy link
Copy Markdown
Collaborator Author

CI on build 83642: console-table.test.ts passed on every lane. The remaining failures are unrelated flakes that all passed on retry or are marked flaky on main:

  • test/js/bun/http/serve.test.ts (darwin x64 and aarch64, EPIPE in the request-body backpressure test; the x64 job passed on retry)
  • test/cli/run/multi-run.test.ts (windows aarch64, EBUSY on temp-dir cleanup)
  • test/js/third_party/@azure/service-bus (windows aarch64, timeout)
  • test/js/node/test/parallel/test-https-server-connections-checking-leak.js (debian x64-asan)
  • test/cli/run/no-orphans.test.ts (darwin x64, timeout)

This diff is 4 lines in TablePrinter::print_table plus a test; it has no interaction with HTTP, process spawning, or TLS. Ready for review.

@github-actions

Copy link
Copy Markdown
Contributor

Closing this PR because it has been inactive for more than 90 days.

@robobun

robobun commented Oct 26, 2026

Copy link
Copy Markdown
Collaborator Author

Replaced by #46986. It also covers Object(Symbol()) and an object whose Symbol.toPrimitive, toString or valueOf returns a Symbol.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants