paperclipai/paperclip · error
Daytona syncOut refusing unparseable or ambiguous symlink…
Error message
Daytona syncOut refusing unparseable or ambiguous symlink entry: ${line} What it means
Fail-closed check in assertTarballEntriesConfined: a tar member whose type flag is 'l' (symlink) did not split cleanly on exactly one ' -> ' separator, so its name and link target are ambiguous. Since the target cannot be verified to stay inside the extraction dir, the syncOut is refused outright.
Solutions
- Fix or exclude the ambiguous symlink entry in the tarball.
Defensive patterns
Strategy: validation
When it happens
Trigger: Thrown at packages/plugins/sandbox-providers/daytona/src/file-sync.ts:247 when the library encounters an invalid state.
Common situations: See trigger scenarios.
AI-assisted analysis of paperclipai/paperclip@3f1d897a7c (2026-08-18).
Data as JSON: /api/errors/cfda5e0ac93cdf50.
Report an issue: GitHub.
Appendix: source
Thrown at packages/plugins/sandbox-providers/daytona/src/file-sync.ts:251
const first = field.indexOf(delimiter);
if (first === -1) return null;
if (field.indexOf(delimiter, first + delimiter.length) !== -1) return null;
return { name: field.slice(0, first), target: field.slice(first + delimiter.length) };
}
/**
* Reject a sandbox-authored tarball before extraction if any member would land
* outside the extraction dir. The archive is produced by the (untrusted) sandbox,
* so `tar -xf` on the host must never be handed an archive whose entries carry
* absolute paths or `../` traversal, nor a symlink/hardlink member whose target
* escapes the tree — the latter would let a follow-up member be written through
* the link to an arbitrary host path. Legitimate in-tree relative links (targets
* that resolve back inside the archive, e.g. `shortcut -> nested/data.txt`) are
* preserved. Parses the `-tvf` verbose listing so both member names and link
* targets are inspected; any unparseable line fails closed.
*/
async function assertTarballEntriesConfined(archivePath: string): Promise<void> {
const { stdout } = await execFileAsync("tar", ["-tvf", archivePath], {
env: { ...process.env, COPYFILE_DISABLE: "1" },
maxBuffer: 32 * 1024 * 1024,
});
const lines = stdout.split("\n").filter((line) => line.trim().length > 0);
for (const line of lines) {
const parsed = parseTarVerboseListingLine(line);
if (!parsed) {
throw new Error(`Daytona syncOut refusing tarball with an unparseable entry listing: ${line}`);
}
const typeFlag = parsed.typeFlag;
let name = parsed.rest;
let linkTarget: string | null = null;
if (typeFlag === "l") {
const split = splitLinkEntryOnce(name, " -> ");
if (!split) throw new Error(`Daytona syncOut refusing unparseable or ambiguous symlink entry: ${line}`);
name = split.name;
linkTarget = split.target;
} else if (typeFlag === "h") {View on GitHub (pinned to 3f1d897a7c)