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

  1. Reject the transfer from the offending peer; hidden characters in paths are a spoofing vector.
  2. 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).
  3. 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

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


AI-assisted analysis of schollz/croc@e25f1bdc04 (2026-08-15). Data as JSON: /api/errors/cf660424e5a547c0. Report an issue: GitHub.