{"record":{"id":"51e0a5c1d753734a","repo":"paperclipai/paperclip","slug":"row-slug-invalid-asset-path","errorCode":null,"errorMessage":"${row.slug}: invalid asset path","messagePattern":"(.+?): invalid asset path","errorType":"console","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"scripts/app-brand-validation.mjs","lineNumber":42,"sourceCode":"}\n\nexport function validateManifest(manifest, readAsset) {\n  if (manifest.schemaVersion !== 1 || !Array.isArray(manifest.providers)) throw new Error(\"Invalid brand manifest\");\n  const keys = new Map();\n  const slugs = new Set();\n  for (const row of manifest.providers) {\n    if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(row.slug) || slugs.has(row.slug)) throw new Error(`Duplicate or invalid slug: ${row.slug}`);\n    slugs.add(row.slug);\n    if (typeof row.provider !== \"string\" || !row.provider.trim()) throw new Error(`${row.slug}: missing provider name`);\n    const aliases = row.aliases ?? [];\n    if (!Array.isArray(aliases) || aliases.some((alias) => typeof alias !== \"string\" || !alias.trim())) throw new Error(`${row.slug}: invalid aliases`);\n    for (const name of [row.slug, row.provider, ...aliases]) {\n      const key = normalizeBrandKey(name);\n      if (keys.has(key) && keys.get(key) !== row.slug) throw new Error(`${row.slug}: ambiguous brand alias ${name}`);\n      keys.set(key, row.slug);\n    }\n    for (const asset of new Set([row.localAsset, ...(row.darkAsset === undefined ? [] : [row.darkAsset])])) {\n      if (typeof asset !== \"string\" || !assetPathPattern.test(asset)) throw new Error(`${row.slug}: invalid asset path`);\n      validateArtwork(readAsset(asset), asset);\n    }\n  }\n  return manifest.providers.length;\n}\n","sourceCodeStart":24,"sourceCodeEnd":48,"githubUrl":"https://github.com/paperclipai/paperclip/blob/3f1d897a7c018d76563a21c6e39c3c9b03933622/scripts/app-brand-validation.mjs#L24-L48","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","solutions":["Correct the asset path to match the required assetPathPattern (check other rows for the expected format).","Ensure the referenced file actually exists at that path in the package.","Remove an unused darkAsset field rather than leaving an invalid value.","Re-run the validation script."],"exampleFix":"// before\n\"localAsset\": \"/abs/path/logo.svg\"\n// after\n\"localAsset\": \"assets/acme/logo.svg\"","handlingStrategy":"validation","validationCode":"const pattern = /^assets\\/[a-z0-9-]+\\/[a-z0-9-]+\\.svg$/; // match assetPathPattern\nfor (const r of manifest.providers)\n  for (const a of [r.localAsset, r.darkAsset].filter(Boolean))\n    if (typeof a !== 'string' || !pattern.test(a)) throw new Error(`${r.slug}: bad asset path ${a}`);","typeGuard":"const hasValidAssetPath = (row, pattern) => typeof row?.localAsset === 'string' && pattern.test(row.localAsset) && (row.darkAsset === undefined || (typeof row.darkAsset === 'string' && pattern.test(row.darkAsset)));","tryCatchPattern":"try { validateManifest(manifest); } catch (e) {\n  if (e.message.includes('invalid asset path')) {\n    const slug = e.message.split(':')[0];\n    console.error(`Fix asset path format for ${slug} to match assetPathPattern`);\n  } else throw e;\n}","preventionTips":["Keep asset files under the expected directory layout and use relative paths only.","Never use absolute paths or ../ segments in asset fields.","Update manifest paths whenever asset files are renamed; do it in the same commit."],"tags":["manifest","validation","assets","path"],"backgroundTag":"invalid-argument-format","analyzedSha":"3f1d897a7c018d76563a21c6e39c3c9b03933622","analyzedAt":"2026-09-18T08:03:59.046Z","contentChangedAt":"2026-09-18T08:03:59.046Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}