nexu-io/open-design · error · ArtifactPublicationBlockedError
ARTIFACT_PUBLICATION_BLOCKED
ARTIFACT_PUBLICATION_BLOCKED
Error message
Artifact still contains unresolved pitch-deck placeholders: ${list}. Provide the required pitch facts before publishing. What it means
Thrown as ArtifactPublicationBlockedError (code ARTIFACT_PUBLICATION_BLOCKED) by assertArtifactPublicationAllowed() when an html or deck artifact body still contains one of the pitch-deck template placeholders ('Name to confirm', '$X.XM', 'Replace this panel with', 'Replace role placeholders', 'Your form answer only said'). It is the defense-in-depth guard for the html-ppt-pitch-deck example template, catching agents that bypassed the structured od.inputs contract.
Source
Thrown at apps/daemon/src/artifacts/publication-guard.ts:80
if (!text) return [];
return UNRESOLVED_ARTIFACT_PLACEHOLDERS.filter((placeholder) =>
text.includes(placeholder),
);
}
export function shouldBlockArtifactPublication(value: unknown): boolean {
return findUnresolvedArtifactPlaceholders(value).length > 0;
}
export function buildArtifactPublicationBlockedMessage(placeholders: readonly string[]): string {
const list = placeholders.length > 0 ? placeholders.join(', ') : 'unknown placeholders';
return `Artifact still contains unresolved pitch-deck placeholders: ${list}. Provide the required pitch facts before publishing.`;
}
export function assertArtifactPublicationAllowed(value: unknown): void {
const placeholders = findUnresolvedArtifactPlaceholders(value);
if (placeholders.length > 0) {
throw new ArtifactPublicationBlockedError(placeholders);
}
}
function stringifyArtifactContent(value: unknown): string {
if (typeof value === 'string') return value;
if (Buffer.isBuffer(value)) return value.toString('utf8');
if (value instanceof Uint8Array) return Buffer.from(value).toString('utf8');
return '';
}
View on GitHub (pinned to 5be4028344)
Solutions
- Provide the required pitch facts through the plugin's structured od.inputs so the placeholders are substituted before publication.
- Manually replace each listed placeholder in the body with real content before publishing.
- If the listed strings are legitimate product copy (not unfilled slots), rename them in the source so they no longer collide with the placeholder set.
Example fix
// before — body still has placeholder '<h2>Funding: $X.XM</h2>' // after — real value substituted via od.inputs '<h2>Funding: $4.5M</h2>'
Defensive patterns
Strategy: validation
Validate before calling
import { findUnresolvedArtifactPlaceholders } from './artifacts/publication-guard.js';
const placeholders = findUnresolvedArtifactPlaceholders(body);
if (placeholders.length > 0) {
throw new Error(`unresolved placeholders: ${placeholders.join(', ')}`);
}
// safe to publish Type guard
import { UNRESOLVED_ARTIFACT_PLACEHOLDERS } from './artifacts/publication-guard.js';
function isFreeOfPitchPlaceholders(text: string): boolean {
return !UNRESOLVED_ARTIFACT_PLACEHOLDERS.some((p) => text.includes(p));
} Try / catch
try {
await publishArtifact(body);
} catch (e) {
if (e instanceof ArtifactPublicationBlockedError) {
// prompt for pitch facts, substitute, then retry
}
throw e;
} Prevention
- Drive pitch-deck creation through the html-ppt-pitch-deck plugin and its od.inputs so placeholders are filled upstream.
- Run findUnresolvedArtifactPlaceholders on the body before exposing a Publish action.
- Treat the placeholder set as reserved strings; do not use them as literal product copy.
When it happens
Trigger: Publishing (writing to the project as a finished artifact) an HTML or deck body produced from the html-ppt-pitch-deck example without first filling in the fundraising facts through od.inputs. The guard runs at the file-write boundary for html/deck kinds only.
Common situations: Agent routed the deck through od-default instead of the official plugin, skipping od.inputs; user previewed a deck and hit 'publish' before answering the pitch facts; copy of the example template whose placeholders were never replaced.
Related errors
- ARTIFACT_MANIFEST_INVALID
- ARTIFACT_MANIFEST_REQUIRED
- invalid JSON in ${filePath}: ${message}
- ${filePath} must contain a JSON object
- sourceKind must be one of upload, url, repo, connector, arti
AI-assisted analysis of nexu-io/open-design@5be4028344 (2026-08-12).
Data as JSON: /api/errors/c1d9353e4875be04.
Report an issue: GitHub.