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

  1. Provide the required pitch facts through the plugin's structured od.inputs so the placeholders are substituted before publication.
  2. Manually replace each listed placeholder in the body with real content before publishing.
  3. 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

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


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