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
- Always pass the refreshId exactly as returned by acquireLiveArtifactRefreshLock() (metadata.refreshId).
- If you persisted the id externally, store the full `refresh-NNNNNN` string, not just the ordinal number.
- Do not construct refresh ids client-side; acquire a lock to obtain a fresh one.
- 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
- Always source refreshId from acquireLiveArtifactRefreshLock().metadata.refreshId.
- Persist the full `refresh-NNNNNN` string externally, never the bare number.
- Do not hand-construct refresh ids.
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
- connector refresh source requires connector metadata
- connector refresh output must be a JSON object
- refresh source ${source.type} is not supported yet
- No refresh source is available yet.
- Refresh is disabled for this artifact source.
AI-assisted analysis of nexu-io/open-design@5be4028344 (2026-08-12).
Data as JSON: /api/errors/d7dd99eebc41cd07.
Report an issue: GitHub.