{"record":{"id":"01f918f49eeeca12","repo":"paperclipai/paperclip","slug":"row-slug-ambiguous-brand-alias-name","errorCode":null,"errorMessage":"${row.slug}: ambiguous brand alias ${name}","messagePattern":"(.+?): ambiguous brand alias (.+?)","errorType":"console","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"scripts/app-brand-validation.mjs","lineNumber":38,"sourceCode":"    || /\\b(?:[\\w-]+:)?href\\s*=\\s*[\"'](?!#)/i.test(svg)\n    || /@import|@media|url\\(\\s*[\"']?(?!#)[^)]*\\)|(?:javascript|data|https?):|\\\\/i.test(svg.replace(/xmlns(?::[\\w-]+)?\\s*=\\s*[\"'][^\"']*[\"']/gi, \"\"))) {\n    throw new Error(`${filename}: unsafe SVG feature, external resource, theme media query, or embedded raster wrapper`);\n  }\n}\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":20,"sourceCodeEnd":48,"githubUrl":"https://github.com/paperclipai/paperclip/blob/3f1d897a7c018d76563a21c6e39c3c9b03933622/scripts/app-brand-validation.mjs#L20-L48","documentation":"The app brand validation script throws this when two different provider rows normalize to the same brand key via normalizeBrandKey (slug, provider name, or any alias colliding across rows). Ambiguous aliases would make brand lookups non-deterministic, so the manifest is rejected.","triggerScenarios":"Running validateManifest where row A's slug/provider/alias normalizes to the same key as row B's (keys.has(key) && keys.get(key) !== row.slug), e.g. two providers aliasing \"GitHub\" or a provider name equal to another row's slug.","commonSituations":"Adding a new provider whose alias duplicates an existing brand; case/spacing variants like \"acme\" vs \"Acme\" normalizing to the same key; renaming a provider so it now collides with another slug.","solutions":["Find the colliding name from the message and change the alias on the newer row to something unambiguous.","If both rows genuinely refer to the same provider, merge the rows instead of duplicating.","Check normalization rules (case/punctuation folding) and pick a name that stays distinct after normalization.","Re-run the validation script to confirm no further collisions."],"exampleFix":"// before (collides with provider \"acme\")\n{ \"slug\": \"acme-cloud\", \"provider\": \"Acme Cloud\", \"aliases\": [\"Acme\"] }\n// after\n{ \"slug\": \"acme-cloud\", \"provider\": \"Acme Cloud\", \"aliases\": [\"Acme Cloud Platform\"] }","handlingStrategy":"validation","validationCode":"function norm(s){return s.toLowerCase().replace(/[^a-z0-9]/g,'');}\nconst seen = new Map();\nfor (const r of manifest.providers)\n  for (const n of [r.slug, r.provider, ...(r.aliases ?? [])]) {\n    const k = norm(n);\n    if (seen.has(k) && seen.get(k) !== r.slug) throw new Error(`Ambiguous alias ${n}`);\n    seen.set(k, r.slug);\n  }","typeGuard":null,"tryCatchPattern":"try { validateManifest(manifest); } catch (e) {\n  if (e.message.includes('ambiguous brand alias')) {\n    console.error('Rename or merge the colliding provider/alias before retrying');\n  } else throw e;\n}","preventionTips":["Search the existing manifest (including aliases) before adding a new provider name.","Prefer fully qualified brand names as aliases to reduce normalization collisions.","Run validation in CI to catch collisions at PR time."],"tags":["manifest","validation","naming","collision"],"backgroundTag":"schema-validation-failed","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"}