Skip to content

fix: print user keys that shadow reserved key names as regular fields#208

Open
troclaux wants to merge 1 commit into
charmbracelet:mainfrom
troclaux:fix/reserved-key-fallthrough
Open

fix: print user keys that shadow reserved key names as regular fields#208
troclaux wants to merge 1 commit into
charmbracelet:mainfrom
troclaux:fix/reserved-key-fallthrough

Conversation

@troclaux

Copy link
Copy Markdown

Problem

When using gum log --structured, passing a key named time causes that key-value pair to be silently dropped in the text and JSON formatters.

Reproduction (from charmbracelet/gum#967):

gum log --structured "Test log message" time 0 normal_var 1 time 2
# prints: Test log message normal_var=1
# expected: Test log message time=0 normal_var=1 time=2

The same silent-drop affects any user key whose name matches a built-in key (time, level, caller, prefix) when the value's type does not match the expected built-in type.

Root cause

In textFormatter and jsonFormatterRoot, each built-in key case does a type assertion and renders the value in a special style. When the assertion fails, the code falls through with no output. The logfmtFormatter already handles this correctly by always encoding the key-value regardless of the type assertion result.

Fix

  • text.go: Extracts the default KV-writing logic into a writeKVPair helper and calls it as the else branch for TimestampKey, LevelKey, CallerKey, and PrefixKey.
  • json.go: Adds an else branch calling the existing jsonFormatterItem helper for the same set of keys.
  • text_test.go: Adds TestReservedKeyNonBuiltinValue to cover the reported scenario.

MessageKey is intentionally left unchanged — a user passing msg as a structured key would produce a duplicate message field, which is a separate discussion.

@troclaux troclaux force-pushed the fix/reserved-key-fallthrough branch from 443b191 to d4c5671 Compare May 10, 2026 23:56
When a user-supplied key has the same string value as a built-in key
(TimestampKey/"time", LevelKey/"level", CallerKey/"caller",
PrefixKey/"prefix") but carries a value of a different type, the text and
JSON formatters silently dropped the key-value pair because the type
assertion failed with no fallback.

The logfmt formatter already handled this correctly by falling through to
EncodeKeyval. This commit applies the same principle to the text and JSON
formatters:

- text.go: extracts the default KV-writing logic into writeKVPair and
  calls it as the else branch for each affected special-key case.
- json.go: adds an else branch calling the existing jsonFormatterItem
  helper for each affected special-key case.

Fixes: gum issue charmbracelet/gum#967
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant