cli: report ssh terminfo cache failures

A state directory with the wrong permissions left the terminfo cache
failing with errors that named no path, so there was nothing to act on:

    $ ghostty +ssh-cache --add=user@host
    Error: Unable to add 'user@host' to cache. Error: error.AccessDenied

Every +ssh-cache failure now names its cache file, and +ssh no longer
swallows cache-related errors.

Error messages in these actions are also lowercased after the "Error: "
prefix and append the error with ": {t}" rather than a second "Error: ".

Ref: https://github.com/ghostty-org/ghostty/issues/9393#issuecomment-5145799368
This commit is contained in:
Jon Parise
2026-07-31 15:31:58 -04:00
parent 4d605bf0d8
commit 8fca64957b
2 changed files with 110 additions and 23 deletions

View File

@@ -245,14 +245,24 @@ fn runInner(
const cache: ?DiskCache = if (opts.cache) cache: {
const path = DiskCache.defaultPath(alloc, "ghostty") catch |err| {
warnPrint(stderr, "ghostty terminfo cache unavailable: {}", .{err});
warnPrint(stderr, "ghostty terminfo cache unavailable: {t}", .{err});
break :session .{ .term = "xterm-256color" };
};
break :cache .{ .path = path };
} else null;
if (cache) |c| {
if (c.contains(alloc, dest) catch false) {
const cached = c.contains(alloc, dest) catch |err| cached: {
// An invalid key is a bad destination, not a bad cache.
if (err != error.InvalidCacheKey) warnPrint(
stderr,
"unable to add '{s}' to the cache: {t}",
.{ c.path, err },
);
break :cached false;
};
if (cached) {
verbosePrint(opts, stderr, "dest: {s} (cached, skipping install)", .{dest});
break :session .{ .term = "xterm-ghostty" };
} else {
@@ -266,7 +276,7 @@ fn runInner(
stderr.flush() catch {};
installRemoteTerminfo(alloc, opts, stderr) catch |err| {
warnPrint(stderr, "failed to install terminfo: {}", .{err});
warnPrint(stderr, "failed to install terminfo: {t}", .{err});
break :session .{ .term = "xterm-256color" };
};
break :session .{
@@ -297,7 +307,7 @@ fn runInner(
verbosePrint(opts, stderr, "exec: {f}", .{Joined{ .items = argv }});
const exit_code = childExec(argv) catch |err| {
try stderr.print("Error: failed to run {s}: {}\n", .{ argv[0], err });
try stderr.print("Error: failed to run {s}: {t}\n", .{ argv[0], err });
return 1;
};
verbosePrint(opts, stderr, "exit: {d}", .{exit_code});
@@ -309,8 +319,23 @@ fn runInner(
.real,
).toSeconds())) |_| {
verbosePrint(opts, stderr, "cache: wrote {s}", .{entry.dest});
} else |err| {
log.debug("cache add failed for '{s}': {}", .{ entry.dest, err });
} else |err| switch (err) {
// An uncacheable destination was already reported above, and a
// lock held by a concurrent `+ssh` resolves itself.
error.InvalidCacheKey,
error.CacheLocked,
=> verbosePrint(
opts,
stderr,
"cache: skipped {s}: {t}",
.{ entry.dest, err },
),
else => warnPrint(
stderr,
"unable to add '{s}' to the cache '{s}': {t}",
.{ entry.dest, entry.cache.path, err },
),
}
};