nexu-io/open-design · error · Error

invalid live artifact refresh id

Error message

invalid live artifact refresh id

What it means

Thrown by parseRefreshOrdinal() when a refreshId does not match the canonical shape `/^refresh-(\d+)$/`. Refresh ids are minted only by formatRefreshId() as `refresh-NNNNNN` (zero-padded to 6 digits), so any other shape means the caller fabricated or corrupted the id. This is the regex-fail branch (line 450); the numeric-range branch is a separate throw at line 453.

Source

Thrown at apps/daemon/src/live-artifacts/store.ts:450

  };
  if (finishedAt !== undefined) entry.finishedAt = finishedAt;
  if (durationMs !== undefined) entry.durationMs = durationMs;
  if (options.source !== undefined) entry.source = options.source;
  if (options.error !== undefined) entry.error = compactLiveArtifactRefreshError(options.error);
  if (options.metadata !== undefined) entry.metadata = options.metadata;
  return entry;
}

function formatRefreshId(refreshOrdinal: number): string {
  if (!Number.isSafeInteger(refreshOrdinal) || refreshOrdinal < 1) {
    throw new Error('invalid live artifact refresh ordinal');
  }
  return `refresh-${refreshOrdinal.toString().padStart(6, '0')}`;
}

function parseRefreshOrdinal(refreshId: string): number {
  const match = /^refresh-(\d+)$/.exec(refreshId);
  if (match === null) throw new Error('invalid live artifact refresh id');
  const refreshOrdinal = Number(match[1]);
  if (!Number.isSafeInteger(refreshOrdinal) || refreshOrdinal < 1) {
    throw new Error('invalid live artifact refresh id');
  }
  return refreshOrdinal;
}

function defaultRefreshState(projectId: string, artifactId: string): LiveArtifactRefreshState {
  return { schemaVersion: 1, projectId, artifactId, nextRefreshOrdinal: 1 };
}

function normalizeRefreshState(value: unknown, projectId: string, artifactId: string): LiveArtifactRefreshState {
  if (!value || typeof value !== 'object' || Array.isArray(value)) {
    throw validationError('refresh-state.json', 'live artifact refresh state must be an object');
  }
  const raw = value as Record<string, unknown>;
  if (raw.schemaVersion !== 1) throw validationError('refresh-state.json.schemaVersion', 'live artifact refresh state schemaVersion must be 1');
  if (raw.projectId !== projectId) throw validationError('refresh-state.json.projectId', 'live artifact refresh state projectId does not match requested project');

View on GitHub (pinned to 5be4028344)

Solutions

  1. Always pass the refreshId exactly as returned by acquireLiveArtifactRefreshLock() (metadata.refreshId).
  2. If you persisted the id externally, store the full `refresh-NNNNNN` string, not just the ordinal number.
  3. Do not construct refresh ids client-side; acquire a lock to obtain a fresh one.
  4. If state files are corrupted, delete refresh-state.json and refresh.lock.json and re-acquire.

Example fix

// before
markLiveArtifactRefreshCommitted({ ..., refreshId: '1' });
// after
const lock = await acquireLiveArtifactRefreshLock({ ... });
markLiveArtifactRefreshCommitted({ ..., refreshId: lock.metadata.refreshId }); // 'refresh-000001'
Defensive patterns

Strategy: validation

Validate before calling

const REFRESH_ID = /^refresh-(\d+)$/;

function isRefreshIdShape(value: unknown): value is string {
  return typeof value === 'string' && REFRESH_ID.test(value);
}

if (!isRefreshIdShape(refreshId)) {
  throw new Error(`refreshId must look like 'refresh-NNNNNN'; got ${JSON.stringify(refreshId)}`);
}

Type guard

function isRefreshId(value: unknown): value is `refresh-${string}` {
  return typeof value === 'string' && /^refresh-\d+$/.test(value);
}

Prevention

When it happens

Trigger: Passing `refresh-1` without padding is actually accepted by the regex (\d+ matches), but passing `refresh_000001`, `refresh-000001a`, `r-000001`, `000001`, or an empty string fails the regex and throws here. Also triggered by passing a briefDraftId or lockId where a refreshId is expected.

Common situations: Caller stores the wrong field from the lock acquisition result; client increments its own counter and formats it inconsistently; log/state file was hand-edited;混淆 between briefDraftId, lockId, and refreshId.

Related errors


AI-assisted analysis of nexu-io/open-design@5be4028344 (2026-08-12). Data as JSON: /api/errors/d7dd99eebc41cd07. Report an issue: GitHub.