heygen-com/hyperframes · error · Error

ref "${refInput}" has no node id — share a link with ?node-i

Error message

ref "${refInput}" has no node id — share a link with ?node-id=… or use fileKey:nodeId

What it means

Thrown by requireNodeRef() in figma asset import. It calls parseFigmaRef(refInput); if no nodeId is resolved it refuses. parseFigmaRef yields no nodeId for: a bare fileKey (no colon), a token like `fileKey:` (empty node segment), or a URL lacking a non-empty `?node-id=` query param. The message points you at the two shapes that DO carry a node id.

Source

Thrown at packages/cli/src/commands/figma/asset.ts:70

/**
 * Flatten CLI positionals into asset refs. Comma-splits bare
 * `fileKey:nodeId` tokens (so `asset A,B` batches) but leaves URL tokens
 * whole — a figma URL can carry commas in its query (multi-select
 * `node-id=1:2,3:4`), and splitting those would tear the URL apart. To batch
 * URLs, pass them as separate positional args.
 */
export function gatherAssetRefs(positionals: string[]): string[] {
  return positionals
    .flatMap((r) => (/^https?:/i.test(r.trim()) ? [r] : r.split(",")))
    .map((r) => r.trim())
    .filter((r) => r.length > 0);
}

function requireNodeRef(refInput: string): { fileKey: string; nodeId: string } {
  const ref = parseFigmaRef(refInput);
  if (!ref.nodeId)
    throw new Error(
      `ref "${refInput}" has no node id — share a link with ?node-id=… or use fileKey:nodeId`,
    );
  return { fileKey: ref.fileKey, nodeId: ref.nodeId };
}

/** Cache hit per spec §5 (fileKey:nodeId:format:scale:version). Check EVERY
 * row for the node — a node can carry several format/scale/version tuples,
 * and the oldest-row shortcut minted duplicates forever. Reuse requires the
 * frozen file to still exist; a deleted file falls through to re-import.
 * Metadata supplied on a re-import upserts rather than being discarded. */
function reuseExisting(
  fileKey: string,
  nodeId: string,
  opts: AssetImportOptions,
  version: string,
  deps: AssetImportDeps,
  description: string | undefined,
  entity: string | undefined,

View on GitHub (pinned to c2996c8626)

Solutions

  1. Right-click the frame in figma and 'Copy link' so the URL includes ?node-id=...
  2. Pass fileKey:nodeId form, e.g. aBcDeF:1:2 (colons or dashes both accepted)
  3. If batching, ensure every positional/comma-split ref resolves to a node id

Example fix

// before
hyperframes figma asset aBcDeF
// after
hyperframes figma asset aBcDeF:1:2
// or
hyperframes figma asset 'https://www.figma.com/design/aBcDeF/Name?node-id=1-2'
Defensive patterns

Strategy: type-guard

Validate before calling

import { parseFigmaRef } from '@hyperframes/core/figma';
const ref = parseFigmaRef(refInput);
if (!ref.nodeId) {
  throw new Error(`ref ${refInput} needs a node id; use fileKey:nodeId or a ?node-id= URL`);
}

Type guard

import { parseFigmaRef } from '@hyperframes/core/figma';
function refHasNodeId(refInput: string): boolean {
  try { return Boolean(parseFigmaRef(refInput).nodeId); }
  catch { return false; }
}

Prevention

When it happens

Trigger: `figma asset <fileKey>` (bare key, no colon); `figma asset https://www.figma.com/design/ABC/Name` (file URL with no node-id query); `figma asset ABC:` (trailing colon, empty node). Node ids from a URL use dashes (e.g. 1:2) which parseFigmaRef normalizes via dashes->colons.

Common situations: Copying the file URL from the address bar instead of using 'Copy link' on a specific frame (which includes ?node-id=); manually stripping query parameters; passing a fileKey intended for a different subcommand.

Related errors


AI-assisted analysis of heygen-com/hyperframes@c2996c8626 (2026-08-12). Data as JSON: /api/errors/c1bf15230fcca5ab. Report an issue: GitHub.