chatboxai/chatbox · warning · Error
duplicate file path for "${name}": ${resolved.normalizedPath
Error message
duplicate file path for "${name}": ${resolved.normalizedPath} What it means
Thrown by prepareSnapshotFiles when two entries in the files array normalize to the same path (e.g. 'a/b' and 'a/./b', or 'a\\b' vs 'a/b' after splitting path.sep and joining '/'). seenPaths is a Set keyed by normalizedPath, so the second occurrence aborts the snapshot write to prevent overwriting ambiguity.
Source
Thrown at src/main/skills/builtin-sync.ts:183
const target = path.resolve(skillDir, normalized)
const root = path.resolve(skillDir)
if (target !== root && !target.startsWith(root + path.sep)) return null
return { target, normalizedPath: normalized.split(path.sep).join('/') }
}
function prepareSnapshotFiles(name: string, skillDir: string, files: RemoteSkillFile[]): SnapshotFileTarget[] {
const targets: SnapshotFileTarget[] = []
const seenPaths = new Set<string>()
for (const file of files) {
if (!file || typeof file.path !== 'string' || typeof file.content !== 'string') {
throw new Error(`invalid file entry for "${name}"`)
}
const resolved = resolveSnapshotFilePath(skillDir, file.path)
if (!resolved) {
throw new Error(`invalid file path for "${name}": ${file.path}`)
}
if (seenPaths.has(resolved.normalizedPath)) {
throw new Error(`duplicate file path for "${name}": ${resolved.normalizedPath}`)
}
seenPaths.add(resolved.normalizedPath)
targets.push({ file, target: resolved.target, normalizedPath: resolved.normalizedPath })
}
return targets
}
/** 将 skill 内容写入快照目录的 SKILL.md(frontmatter + body)和附属文件,与 parser 的解析格式一致。 */
function writeSnapshotSkill(
name: string,
metadata: SkillMetadata,
body: string,
files: RemoteSkillFile[] = [],
options: { replaceDir?: boolean } = {}
): void {
const skillDir = path.join(getBuiltinSkillsDir(), name)
const fileTargets = prepareSnapshotFiles(name, skillDir, files)
if (options.replaceDir) {View on GitHub (pinned to 81571269ad)
Solutions
- Dedupe the files array by normalized path before sending/persisting the manifest.
- Treat the duplicate as a server bug: log the offending normalizedPath and reject the skill.
- If duplicates are benign, keep the first and skip subsequent entries instead of throwing.
- Canonicalize paths (strip './', normalize separators) at manifest build time.
Example fix
// before
if (seenPaths.has(resolved.normalizedPath)) throw new Error(`duplicate file path for "${name}": ${resolved.normalizedPath}`)
// after: last-writer-wins with a warning
if (seenPaths.has(resolved.normalizedPath)) { log.warn(`duplicate file path for "${name}": ${resolved.normalizedPath}`); continue } Defensive patterns
Strategy: validation
Validate before calling
const seen = new Set<string>()
for (const f of files) { const n = f.path.split(path.sep).join('/'); if (seen.has(n)) { dedupeOrReject(n) } else seen.add(n) } Type guard
function isDuplicateFilePath(e: unknown): e is Error { return e instanceof Error && e.message.startsWith('duplicate file path for') } Try / catch
try { await syncSkill(name, files) } catch (e) { if (isDuplicateFilePath(e)) { files = dedupeByNormalizedPath(files); await syncSkill(name, files); return } throw e } Prevention
- Dedupe the manifest by normalized path before publishing.
- Canonicalize separators ('/' everywhere) at build time.
- Decide policy (reject vs last-wins) explicitly; don't let the throw surprise users.
When it happens
Trigger: Remote skill manifest lists the same logical file twice with syntactically different paths; case-insensitive filesystem collision (the normalizer does not lowercase, so 'A.md' and 'a.md' collide only on disk, not here — but 'a/b' and 'a/./b' or mixed separators do collide here).
Common situations: Manifest generated by merging two sources without dedupe; 'folder/file' and 'folder\file' both present on a cross-platform skill; redundant index files; server-side packaging bug.
Related errors
- invalid file entry for "${name}"
- invalid file path for "${name}": ${file.path}
- Session attachment ${id} not found
- Only failed session attachments can be retried
- Attachment content not found or empty
AI-assisted analysis of chatboxai/chatbox@81571269ad (2026-08-12).
Data as JSON: /api/errors/e18dbfedc784823b.
Report an issue: GitHub.