schollz/croc · error · Error
Remote path contains a non-printable character: ${value}
Error message
Remote path contains a non-printable character: ${value} What it means
cleanSegments() checks each path segment's characters against the Unicode 'Other' category (\P{C}: control, format, surrogate, private-use, unassigned) and rejects any segment containing one. Only printable characters are allowed in remote filenames, matching Go's unicode.IsPrint expectations and preventing terminal/download-sink confusion.
Source
Thrown at web/src/protocol/metadata.ts:17
import { base64ToBytes } from "./bytes";
import type {
OfferedFile,
SenderInfoWire,
TransferOffer,
WireFileInfo,
} from "./types";
function cleanSegments(value: string) {
const replaced = value.replaceAll("\\", "/");
if (replaced.includes("\0")) throw new Error("A remote path contains a null byte");
const segments: string[] = [];
for (const segment of replaced.split("/")) {
if (segment === "" || segment === ".") continue;
if (segment === "..") throw new Error(`Remote path escapes the destination: ${value}`);
if ([...segment].some((character) => !/\P{C}/u.test(character))) {
throw new Error(`Remote path contains a non-printable character: ${value}`);
}
segments.push(segment);
}
return segments;
}
export function normalizeFolder(value = ".") {
if (/^(?:[a-zA-Z]:|\/)/.test(value)) {
throw new Error(`Remote path must be relative: ${value}`);
}
const segments = cleanSegments(value);
const normalized = segments.join("/") || ".";
if (normalized.includes(".ssh")) {
throw new Error(`Remote path is not allowed: ${value}`);
}
return normalized;
}
View on GitHub (pinned to e25f1bdc04)
Solutions
- Reject the transfer from the offending peer; hidden characters in paths are a spoofing vector.
- As a sender, sanitize filenames to printable Unicode before offering (see normalizeOutgoingFileName for the outgoing equivalent that also maps \p{Z} separators to ASCII space).
- If a legit file triggers it, rename the file on the sender to remove the invisible characters and re-offer.
Defensive patterns
Strategy: validation
Validate before calling
function isPrintablePath(value: string): boolean {
return [...value.replaceAll("\\", "/")].every((ch) => /\P{C}/u.test(ch));
} Try / catch
try {
const offer = validateSenderInfo(info);
} catch (error) {
if (error instanceof Error && error.message.startsWith("Remote path contains a non-printable character")) {
rejectOffer("non-printable characters in path");
return;
}
throw error;
} Prevention
- Display remote filenames with escapes/JSON encoding so hidden characters cannot spoof UI.
- Sanitize outgoing filenames to printable Unicode before building offers.
- Treat zero-width and bidi control characters in filenames as a spoofing red flag.
When it happens
Trigger: A peer-supplied path segment containing control characters (e.g. U+0007 bell, U+001B escape), zero-width/format characters (U+200B, U+202E RTL override), lone surrogates, or other non-printable code points. Note the check runs on each character, so even one hidden character anywhere in a segment triggers it.
Common situations: Filenames copied from terminal output with embedded ANSI escapes; spoofing attempts using bidi/zero-width characters to disguise extensions; macOS filenames with unusual Unicode; hostile sender deliberately testing the sanitization gate.
Related errors
- A remote path contains a null byte
- Remote path escapes the destination: ${value}
- Remote path must be relative: ${value}
- Remote path is not allowed: ${value}
- Remote filename must be a basename: ${nameValue}
AI-assisted analysis of schollz/croc@e25f1bdc04 (2026-08-15).
Data as JSON: /api/errors/cf660424e5a547c0.
Report an issue: GitHub.