paperclipai/paperclip · error · Error

: invalid asset path

Error message

${row.slug}: invalid asset path

What it means

The app brand validation script throws this when a provider row's localAsset (or darkAsset) is not a string or does not match the manifest's assetPathPattern. Asset paths must follow the expected layout so the validator can read and inspect the artwork.

Solutions

  1. Correct the asset path to match the required assetPathPattern (check other rows for the expected format).
  2. Ensure the referenced file actually exists at that path in the package.
  3. Remove an unused darkAsset field rather than leaving an invalid value.
  4. Re-run the validation script.

Example fix

// before
"localAsset": "/abs/path/logo.svg"
// after
"localAsset": "assets/acme/logo.svg"
Defensive patterns

Strategy: validation

Validate before calling

const pattern = /^assets\/[a-z0-9-]+\/[a-z0-9-]+\.svg$/; // match assetPathPattern
for (const r of manifest.providers)
  for (const a of [r.localAsset, r.darkAsset].filter(Boolean))
    if (typeof a !== 'string' || !pattern.test(a)) throw new Error(`${r.slug}: bad asset path ${a}`);

Type guard

const hasValidAssetPath = (row, pattern) => typeof row?.localAsset === 'string' && pattern.test(row.localAsset) && (row.darkAsset === undefined || (typeof row.darkAsset === 'string' && pattern.test(row.darkAsset)));

Try / catch

try { validateManifest(manifest); } catch (e) {
  if (e.message.includes('invalid asset path')) {
    const slug = e.message.split(':')[0];
    console.error(`Fix asset path format for ${slug} to match assetPathPattern`);
  } else throw e;
}

Prevention

When it happens

Trigger: Running validateManifest where row.localAsset or row.darkAsset is missing/non-string, or is a string that fails assetPathPattern (wrong directory, wrong extension, absolute path, or path traversal style).

Common situations: Renaming asset files without updating the manifest; using an absolute path or ../ escape; adding a darkAsset with a path that doesn't follow the pattern; typos in the extension.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


AI-assisted analysis of paperclipai/paperclip@3f1d897a7c (2026-09-18). Data as JSON: /api/errors/51e0a5c1d753734a. Report an issue: GitHub.

Appendix: source

Thrown at scripts/app-brand-validation.mjs:42

}

export function validateManifest(manifest, readAsset) {
  if (manifest.schemaVersion !== 1 || !Array.isArray(manifest.providers)) throw new Error("Invalid brand manifest");
  const keys = new Map();
  const slugs = new Set();
  for (const row of manifest.providers) {
    if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(row.slug) || slugs.has(row.slug)) throw new Error(`Duplicate or invalid slug: ${row.slug}`);
    slugs.add(row.slug);
    if (typeof row.provider !== "string" || !row.provider.trim()) throw new Error(`${row.slug}: missing provider name`);
    const aliases = row.aliases ?? [];
    if (!Array.isArray(aliases) || aliases.some((alias) => typeof alias !== "string" || !alias.trim())) throw new Error(`${row.slug}: invalid aliases`);
    for (const name of [row.slug, row.provider, ...aliases]) {
      const key = normalizeBrandKey(name);
      if (keys.has(key) && keys.get(key) !== row.slug) throw new Error(`${row.slug}: ambiguous brand alias ${name}`);
      keys.set(key, row.slug);
    }
    for (const asset of new Set([row.localAsset, ...(row.darkAsset === undefined ? [] : [row.darkAsset])])) {
      if (typeof asset !== "string" || !assetPathPattern.test(asset)) throw new Error(`${row.slug}: invalid asset path`);
      validateArtwork(readAsset(asset), asset);
    }
  }
  return manifest.providers.length;
}

View on GitHub (pinned to 3f1d897a7c)