fix(uploads): show the server's reason when a prompt upload fails

A failed upload only showed a count, so a remote session's refused upload read as a mystery instead of a rule the user could act on. The server already returns the reason in its error envelope and the per-file error carries it, so I surface the first one in the toast. One reason is enough when a whole batch fails for the same cause.
This commit is contained in:
JD
2026-10-09 20:13:58 -04:00
parent b59145effd
commit 3be03c6896
2 changed files with 15 additions and 1 deletions
+3 -1
View File
@@ -150,6 +150,7 @@ Object.assign(CodemanApp.prototype, {
const total = files.length;
let done = 0;
let failed = 0;
let failReason = ''; // first server reason, shown in the toast so a failure is not just a count
const results = new Array(total); // preserve selection order for insertion
const progress = () =>
this.showToast(`Uploading ${Math.min(done + 1, total)}/${total} image${total > 1 ? 's' : ''}…`, 'info');
@@ -173,6 +174,7 @@ Object.assign(CodemanApp.prototype, {
results[i] = await this._uploadPasteImage(sessionId, normalized);
} catch (err) {
failed++;
if (!failReason && err && err.message) failReason = err.message;
console.warn('Image upload failed:', err);
results[i] = null;
} finally {
@@ -196,7 +198,7 @@ Object.assign(CodemanApp.prototype, {
// Final status: successes, plus any failures / cap so nothing is silent.
const parts = [];
if (paths.length > 0) parts.push(`${paths.length} image${paths.length > 1 ? 's' : ''} ready`);
if (failed > 0) parts.push(`${failed} failed`);
if (failed > 0) parts.push(failReason ? `${failed} failed: ${failReason}` : `${failed} failed`);
if (capped) parts.push(`max ${this._maxBatchImages} per batch`);
const tone = paths.length > 0 ? (failed > 0 || capped ? 'info' : 'success') : 'error';
this.showToast(parts.join(' · ') || 'No images uploaded', tone);
+12
View File
@@ -238,4 +238,16 @@ describe('image upload insertion policy', () => {
expect(app._uploadPasteImage).toHaveBeenCalledWith('session-b', { path: '/tmp/pane-b.png' });
expect(app._sendInputAsync).toHaveBeenCalledWith('session-b', '/tmp/pane-b.png', { useMux: true });
});
it('shows the server reason in the toast when an upload fails', async () => {
const app = loadImageInputApp();
const reason = 'Prompt uploads are not supported for remote (SSH) sessions';
app._uploadPasteImage = vi.fn(async () => {
throw new Error(reason);
});
await app._uploadAndInsertImages([{ path: '/tmp/shot.png' }]);
expect(app.showToast).toHaveBeenCalledWith(`1 failed: ${reason}`, 'error');
});
});