vercel/hyper · warning · Error
Malformed server response: release name is missing.
Error message
Malformed server response: release name is missing.
What it means
Thrown by Hyper's Linux AutoUpdater (app/auto-updater-linux.ts) after it fetches the configured update feed and parses the response as JSON. The feed contract requires an object with {name, notes, pub_date}, and `name` is the only mandatory field because it is used to construct the release download URL. If the parsed body has no `name` (or `name` is falsy/undefined), the updater cannot proceed and throws. The throw happens inside a resolved promise and is funneled to `emitError`, so it surfaces as an `'error'` event on the autoUpdater EventEmitter rather than a synchronous exception.
Source
Thrown at app/auto-updater-linux.ts:33
this.updateURL = options.url;
}
checkForUpdates() {
if (!this.updateURL) {
return this.emitError('Update URL is not set');
}
this.emit('checking-for-update');
fetch(this.updateURL)
.then((res) => {
if (res.status === 204) {
this.emit('update-not-available');
return;
}
return res.json().then(({name, notes, pub_date}: {name: string; notes: string; pub_date: string}) => {
// Only name is mandatory, needed to construct release URL.
if (!name) {
throw new Error('Malformed server response: release name is missing.');
}
const date = pub_date ? new Date(pub_date) : new Date();
this.emit('update-available', {}, notes, name, date);
});
})
.catch(this.emitError.bind(this));
}
emitError(error: string | Error) {
if (typeof error === 'string') {
error = new Error(error);
}
this.emit('error', error);
}
}
const autoUpdaterLinux = new AutoUpdater();
View on GitHub (pinned to da0c401d7f)
Solutions
- curl -i the exact updateURL from setFeedURL and confirm the JSON body contains a non-empty `name` field; the contract is {name: string, notes?: string, pub_date?: string}.
- If you control the update server, ensure every non-204 release response includes `name` (typically the release tag/version string).
- If using electron-builder, point the feed URL at the JSON it generates (or use electron-updater) instead of a hand-rolled endpoint that omits `name`.
- Register an `autoUpdater.on('error', err => ...)` listener so a malformed feed degrades gracefully instead of surfacing as an unhandled EventEmitter error (Node throws on unhandled 'error' events).
- Verify no proxy/CDN is rewriting the response body; check the `content-type` and raw bytes, not just the status code.
Example fix
// before
autoUpdaterLinux.setFeedURL({url: 'https://example.com/releases/latest'});
autoUpdaterLinux.checkForUpdates();
// server returns {} or {notes, pub_date} with no name -> 'Malformed server response: release name is missing.'
// after — fix the feed to include name AND guard the consumer
// server-side: respond with {"name":"v3.1.0","notes":"...","pub_date":"2026-08-12T00:00:00Z"}
import autoUpdaterLinux from './app/auto-updater-linux';
autoUpdaterLinux.setFeedURL({url: 'https://example.com/releases/latest'});
autoUpdaterLinux.on('error', (err) => {
// graceful degradation — never let an EventEmitter 'error' go unhandled
console.warn('Update check failed:', err.message);
});
autoUpdaterLinux.checkForUpdates(); Defensive patterns
Strategy: try-catch
Validate before calling
// Pre-flight the feed before handing the URL to the AutoUpdater so a malformed
// server never reaches the throw site. Run once at app start or on URL change.
import fetch from 'electron-fetch';
async function assertFeedShape(url: string): Promise<void> {
const res = await fetch(url);
if (res.status === 204) return; // update-not-available, no body needed
if (!res.ok) throw new Error(`Feed HTTP ${res.status}`);
const body = await res.json() as unknown;
if (
typeof body !== 'object' ||
body === null ||
typeof (body as { name?: unknown }).name !== 'string' ||
(body as { name: string }).name.length === 0
) {
throw new Error(`Feed at ${url} is missing a non-empty \`name\` field`);
}
}
// usage
await assertFeedShape(autoUpdaterLinux.getFeedURL());
autoUpdaterLinux.checkForUpdates(); Type guard
// Narrow the parsed feed before touching required fields.
import type { ReleaseFeed } from './release-feed'; // { name: string; notes?: string; pub_date?: string }
function isReleaseFeed(v: unknown): v is ReleaseFeed {
if (typeof v !== 'object' || v === null) return false;
const name = (v as { name?: unknown }).name;
if (typeof name !== 'string' || name.length === 0) return false;
return true;
}
const body: unknown = await res.json();
if (!isReleaseFeed(body)) {
throw new Error('Malformed server response: release name is missing.');
}
// body.name is now string Try / catch
// AutoUpdater is an EventEmitter; the throw is converted to an 'error' event.
// Always attach a listener BEFORE checkForUpdates — Node throws on unhandled 'error'.
import autoUpdaterLinux from './app/auto-updater-linux';
autoUpdaterLinux.once('error', (err: Error) => {
if (/release name is missing/i.test(err.message)) {
// Known shape: feed contract violation. Degrade silently, schedule retry.
console.warn('Update feed malformed, skipping this check:', err.message);
return;
}
// Unknown cause — surface to app-level error reporting.
throw err;
});
autoUpdaterLinux.on('update-available', (_e, notes, name, date) => {
/* ... */
});
autoUpdaterLinux.checkForUpdates(); Prevention
- Pin setFeedURL to an endpoint you control that always emits {name, notes, pub_date} for non-204 responses.
- Never point the feed URL at a raw GitHub web URL or HTML page; use the releases API JSON or electron-builder's generated feed.
- Always register an 'error' listener on the AutoUpdater before calling checkForUpdates — an unhandled EventEmitter 'error' crashes the process.
- Smoke-test the feed with curl in CI whenever you change release tooling, asserting jq -e '.name | type == "string" and length > 0'.
- Treat 204 as the only 'no update' signal; do not return 200 with an empty body.
When it happens
Trigger: Calling `autoUpdaterLinux.checkForUpdates()` after `setFeedURL({url})` where `url` resolves to a 200/2xx response whose JSON body lacks a `name` key (e.g. `{}`, `{notes, pub_date}` without name, a GitHub API rate-limit JSON, a redirect/captive-portal JSON page, or any non-update JSON the server happens to return). A 204 short-circuits to `update-not-available` before this check, so the response must be non-204 with a JSON content-type that parses.
Common situations: Update URL misconfigured to point at a wrong endpoint (HTML landing page whose JSON parse yields empty object, GitHub API release JSON with `tag_name`/`name` mismatch, electron-builder's `latest.yml` served raw as YAML-in-JSON, proxy replacing the body with a block page that still deserializes, CDN serving a stale/malformed manifest after a botched release publish, or a v1/v2 feed schema change that renamed `name` to `version`).
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
AI-assisted analysis of vercel/hyper@da0c401d7f (2026-08-12).
Data as JSON: /api/errors/28d97cf20f856b3f.
Report an issue: GitHub.