heygen-com/hyperframes · error
${failureStage}${attemptDetail}: ${describeFetchFailure(last
Error message
${failureStage}${attemptDetail}: ${describeFetchFailure(lastError)} What it means
fetchForPublish is the shared retry wrapper for all publish-network calls. It retries on generic fetch exceptions (network/DNS/SSL) up to `attempts` times (default 1; staged upload paths use PUBLISH_TRANSPORT_ATTEMPTS=2), but breaks immediately on a request timeout/abort. When every attempt throws (the fetch never returned a Response at all), it throws `${failureStage}${attemptDetail}: ${describeFetchFailure(lastError)}` with the original error on the `.cause`. This is transport-level — distinct from a server HTTP error response (those are [257]/[258]/[259]).
Source
Thrown at packages/cli/src/utils/publishProject.ts:243
createInit: () => RequestInit,
failureStage: string,
attempts = 1,
): Promise<Response> {
if (attempts < 1) throw new RangeError("Publish fetch attempts must be at least 1");
let lastError: unknown;
let attemptsMade = 0;
for (let attempt = 1; attempt <= attempts; attempt += 1) {
attemptsMade = attempt;
try {
return await fetch(input, createInit());
} catch (error) {
lastError = error;
if (isRequestTimeout(error) || attempt === attempts) break;
await waitBeforePublishRetry();
}
}
const attemptDetail = attemptsMade > 1 ? ` after ${attemptsMade} attempts` : "";
throw new Error(`${failureStage}${attemptDetail}: ${describeFetchFailure(lastError)}`, {
cause: lastError instanceof Error ? lastError : undefined,
});
}
export function uploadTimeoutMs(byteLength: number): number {
return Math.max(
PUBLISH_UPLOAD_MIN_TIMEOUT_MS,
Math.ceil((byteLength / PUBLISH_UPLOAD_BYTES_PER_SECOND) * 1000),
);
}
function shouldIgnoreSegment(segment: string): boolean {
return segment.startsWith(".") || IGNORED_DIRS.has(segment) || IGNORED_FILES.has(segment);
}
function createProjectIgnore(rootDir: string): Ignore {
const matcher = ignore().add(DEFAULT_PROJECT_IGNORE);
const ignorePath = join(rootDir, HYPERFRAMES_IGNORE_FILE);View on GitHub (pinned to c2996c8626)
Solutions
- If a proxy is set, retry with NODE_USE_ENV_PROXY=1 (Node 22.21+) — the error appends this hint when relevant.
- Verify network connectivity to the API host: curl -I <api-base-url>.
- For transient issues, simply retry the publish command (the wrapper already retries twice for staged uploads).
- Check DNS resolution and TLS trust store on the host.
- For slow networks, the upload timeout is auto-scaled by archive size (uploadTimeoutMs); ensure the archive is not unexpectedly huge.
Example fix
# before: proxy set but ignored by Node fetch export HTTPS_PROXY=http://corp-proxy:3128 hyperframes publish . # after export NODE_USE_ENV_PROXY=1 hyperframes publish .
Defensive patterns
Strategy: retry
Validate before calling
import { isRequestTimeout } from './publishProject'; // or replicate
async function pingApi(apiBaseUrl: string): Promise<void> {
try {
const r = await fetch(`${apiBaseUrl}/v1/hyperframes/projects`, { signal: AbortSignal.timeout(5000) });
if (!r.ok && r.status >= 500) throw new Error('API unhealthy');
} catch (e) {
throw new Error(`Cannot reach publish API at ${apiBaseUrl}: ${(e as Error).message}`);
}
} Try / catch
try {
return await publishProjectArchive(projectDir, opts);
} catch (err) {
if (err instanceof Error && /after \d+ attempts:/.test(err.message)) {
// transport-level: retry with backoff, or surface proxy hint
if (process.env.HTTPS_PROXY && process.env.NODE_USE_ENV_PROXY !== '1') {
console.error('Set NODE_USE_ENV_PROXY=1 and retry (Node fetch ignores proxy vars otherwise).');
}
await new Promise(r => setTimeout(r, 1000));
return publishProjectArchive(projectDir, opts);
}
throw err;
} Prevention
- If behind a corporate proxy, set NODE_USE_ENV_PROXY=1 (Node 22.21+) so fetch honors HTTPS_PROXY.
- Run a connectivity check to the API host before publishing in CI.
- Keep archives reasonably sized — upload timeout scales with byte length but very large archives are more fragile on flaky links.
When it happens
Trigger: DNS resolution fails for the API host; the TCP connection is refused (API down); TLS handshake fails; the network is unreachable; a timeout fires (AbortSignal.timeout or DOMException TimeoutError) on the first attempt (immediate break, no retry); a proxy is configured via HTTPS_PROXY but Node fetch ignores it without NODE_USE_ENV_PROXY=1 (the error appends a proxySupportHint).
Common situations: Corporate proxy environment where HTTPS_PROXY is set but Node 22's fetch does not honor it without NODE_USE_ENV_PROXY=1; offline or flaky network; the publish API host is temporarily unreachable; a self-signed cert or corporate MITM breaking TLS; slow upload exceeding uploadTimeoutMs on every retry.
Related errors
- figma render download failed: HTTP ${res.status}
- RATE_LIMITED
- [build-zip] npm install into staging failed (status ${result
- BeginFrame probe timeout before ${label}
- Model download failed: ${model}
AI-assisted analysis of heygen-com/hyperframes@c2996c8626 (2026-08-12).
Data as JSON: /api/errors/e1ebd0f8cb3640fc.
Report an issue: GitHub.