From 5dee1a6cafb95ad749f6fc256a43921703b51a62 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Fri, 9 Oct 2026 11:39:54 +0000 Subject: [PATCH 1/2] fix(repl): print every line of a usage error at the prompt A repository or notebook command given wrong arguments returns its usage as a *UsageError, whose Error() is only the first line; the terminal loop printed that line alone, leaving out the line saying what was wrong, while the Jupyter kernel's traceback showed both. The loop now prints every line the error holds, the first under the error prefix as before. Co-Authored-By: jason.han --- .../repl-usage-error-lines.fixed.md | 1 + internal/frontend/repl/loop.go | 13 +++++++++++- internal/frontend/repl/repository_test.go | 20 +++++++++++++++++++ 3 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 changes/unreleased/repl-usage-error-lines.fixed.md diff --git a/changes/unreleased/repl-usage-error-lines.fixed.md b/changes/unreleased/repl-usage-error-lines.fixed.md new file mode 100644 index 0000000000..2cf0c3f474 --- /dev/null +++ b/changes/unreleased/repl-usage-error-lines.fixed.md @@ -0,0 +1 @@ +- **The terminal prompt prints every line of a usage error.** A repository or notebook command given wrong arguments — `%load --id x`, `%load --id=a --id=b`, `%load --tags=a a.ipynb` — printed only the usage line at the prompt, leaving out the line saying what was wrong (`--id is not an option of %load`), which the Jupyter kernel's `UsageError` traceback showed. The prompt now prints the usage and the problem, as the kernel does. diff --git a/internal/frontend/repl/loop.go b/internal/frontend/repl/loop.go index b29ac645b0..01208608c0 100644 --- a/internal/frontend/repl/loop.go +++ b/internal/frontend/repl/loop.go @@ -56,7 +56,7 @@ func Loop(r LineReader, out io.Writer, s *Session) error { printLines(w, metaOut) _ = w.Flush() if merr != nil { - printLines(w, []string{"error: " + merr.Error()}) + printLines(w, metaErrorLines(merr)) _ = w.Flush() } if quit { @@ -124,6 +124,17 @@ func bareExpression(src string) (string, bool) { return trimmed, true } +// metaErrorLines is how the prompt prints a meta command's error: a usage error +// prints every line it holds, the first under the error prefix as the Jupyter +// kernel's value is; any other error prints its message under the prefix. +func metaErrorLines(err error) []string { + var usage *UsageError + if errors.As(err, &usage) && len(usage.Lines) > 0 { + return append([]string{errPrefix + usage.Error()}, usage.Lines[1:]...) + } + return []string{errPrefix + err.Error()} +} + func printLines(w io.Writer, lines []string) { for _, l := range lines { _, _ = io.WriteString(w, l+"\n") diff --git a/internal/frontend/repl/repository_test.go b/internal/frontend/repl/repository_test.go index 10846841a5..b2a67b3463 100644 --- a/internal/frontend/repl/repository_test.go +++ b/internal/frontend/repl/repository_test.go @@ -61,6 +61,26 @@ func wantUsage(t *testing.T, err error, line string) { func joined(lines []string) string { return strings.Join(lines, "\n") } +// TestLoopPrintsEveryLineOfAUsageError drives a repository usage error through +// the terminal loop: the prompt prints the usage line under the error prefix +// and the line saying what was wrong after it, as the kernel's traceback does. +func TestLoopPrintsEveryLineOfAUsageError(t *testing.T) { + repoSession(t) + var out strings.Builder + if err := Loop(&scriptReader{lines: []string{"%load --id x", "%load --id=a --id=b", "%projects now"}}, &out, NewSession()); err != nil { + t.Fatalf("Loop error: %v", err) + } + got := strings.Split(strings.TrimSuffix(out.String(), "\n"), "\n") + want := []string{ + "error: " + usageLoadRepo, "--id is not an option of %load", + "error: " + usageLoadRepo, "--id was given twice", + "error: " + usageProjects, + } + if strings.Join(got, "\n") != strings.Join(want, "\n") { + t.Errorf("the loop printed:\n%s\nwant:\n%s", strings.Join(got, "\n"), strings.Join(want, "\n")) + } +} + func TestRepoShowsAndSetsTheBaseURL(t *testing.T) { s, api := repoSession(t) if got := joined(runMeta(t, s, "%repo")); got != "API base path: "+api.URL() { From 697c1bc5a642f794c9af94dc4f61d45509df9919 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Fri, 9 Oct 2026 12:53:51 +0000 Subject: [PATCH 2/2] refactor(repl): keep usage-error comments to two lines Co-Authored-By: jason.han --- internal/frontend/repl/loop.go | 5 ++--- internal/frontend/repl/repository_test.go | 5 ++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/internal/frontend/repl/loop.go b/internal/frontend/repl/loop.go index 01208608c0..71d096e630 100644 --- a/internal/frontend/repl/loop.go +++ b/internal/frontend/repl/loop.go @@ -124,9 +124,8 @@ func bareExpression(src string) (string, bool) { return trimmed, true } -// metaErrorLines is how the prompt prints a meta command's error: a usage error -// prints every line it holds, the first under the error prefix as the Jupyter -// kernel's value is; any other error prints its message under the prefix. +// metaErrorLines prints a meta command's error: every line of a usage error, +// the first under the error prefix as the kernel's value; other errors as before. func metaErrorLines(err error) []string { var usage *UsageError if errors.As(err, &usage) && len(usage.Lines) > 0 { diff --git a/internal/frontend/repl/repository_test.go b/internal/frontend/repl/repository_test.go index b2a67b3463..3a9c3fb534 100644 --- a/internal/frontend/repl/repository_test.go +++ b/internal/frontend/repl/repository_test.go @@ -61,9 +61,8 @@ func wantUsage(t *testing.T, err error, line string) { func joined(lines []string) string { return strings.Join(lines, "\n") } -// TestLoopPrintsEveryLineOfAUsageError drives a repository usage error through -// the terminal loop: the prompt prints the usage line under the error prefix -// and the line saying what was wrong after it, as the kernel's traceback does. +// TestLoopPrintsEveryLineOfAUsageError drives repository usage errors through the +// terminal loop: the usage line under the error prefix, then what was wrong. func TestLoopPrintsEveryLineOfAUsageError(t *testing.T) { repoSession(t) var out strings.Builder