Skip to content

lore search truncates descriptions by byte count, cutting multi-byte characters mid-rune #741

Closed

Description

@David-Noble-at-work

Feature: #833 — Renderings: the pipeline from result value to stdout

Epic: #740 — Command line interface
Converted by: the Phase 4 TableFormatter work in docs/plans/cli-output-conventions.md

lore search renders its results table with byte arithmetic, so a description containing any non-ASCII
character is truncated at the wrong place and can be cut mid-rune.

The code

cmd/lore/lore/commands.go:536-539:

desc := r.Description
if len(desc) > 50 {
    desc = desc[:47] + "..."
}

Two defects, neither a panic

len(desc) > 50 guards the slice against a range error, so this does not crash. It is wrong in two quieter
ways:

  1. The threshold measures bytes, not characters. A 20-character CJK description is 60 bytes, so it is
    truncated as "too long" while a 50-character ASCII description is not. The user sees an arbitrary cut.
  2. desc[:47] can split a rune. Byte 47 may land inside a multi-byte character, and the output is then
    invalid UTF-8 — rendered as a replacement character by most terminals.

The same byte-vs-width confusion affects alignment: the %-30s column padding counts bytes, so a row with
any wide or multi-byte character does not line up with its neighbours.

Scope

Not only this call site. Any fixed-width fmt.Printf table has the same problem, and runSearch is the one
real table in the tree today (lore list is a stub). The fix is the shared TableFormatter that #740's
Phase 4 introduces: measure in runes, truncate on a rune boundary, and pad by display width rather than byte
count.

Acceptance

  • A description of non-ASCII characters is truncated by character count, not byte count
  • No truncation produces invalid UTF-8
  • Columns align when a row contains multi-byte characters
  • installed is a field the renderer decorates, not a * concatenated onto the name column
    Retracted 2026-09-18: this was never what lore search truncates descriptions by byte count, cutting multi-byte characters mid-rune #741 fixed. §7 rules it directly -- installed stays a
    field that --output json emits, and no rendering folds a boolean into a name column -- so the
    box closes on the ruling rather than on work. Ledger row 14.

Activity

  1. added
    Area:CLICommand-line interface
    Severity:LowCosmetic, or a defect nobody is hitting
    Priority:P3Someday, or on next contact
    Epic:CommandLineOne output convention across every CLI in the suite
    on Aug 29, 2026
  2. David-Noble-at-work commented on Aug 30, 2026

    @David-Noble-at-work
    ContributorAuthor

    Half of this is fixed on cli-output-conventions (PR #744), and half is not. Recording which, so the issue is not read as either done or untouched.

    Fixed — the alignment half. pkg/result.TableFormatter renders through text/tabwriter, which measures cell widths in runes. The hand-rolled %-30s this issue describes counts bytes, so every row containing a multi-byte character misaligned.

    Not fixed — the truncation half. lore's runSearch still slices descriptions by byte count, which is what cuts a multi-byte character mid-rune. That is the defect in this issue's title and it is untouched.

    The remaining work is one task on epic #740: convert runSearch to the shared formatter. Doing so fixes the truncation as a consequence rather than as a separate patch — the conversion is a bug fix as much as a refactor, which is why the plan sequences it first among lore's commands.

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

Metadata

Metadata

Labels

Area:CLICommand-line interfaceEpic:CommandLineOne output convention across every CLI in the suitePriority:P3Someday, or on next contactSeverity:LowCosmetic, or a defect nobody is hittingbugSomething isn't working

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions