paperclipai/paperclip · error · Error

Could not determine remote file size for ${remotePath}

Error message

Could not determine remote file size for ${remotePath}

What it means

Thrown by the readFile method of the command-managed runtime client when 'wc -c < <remotePath>' returns a value that is not a finite non-negative integer. This size probe is the first step of a chunked remote read: it determines how many dd-based chunks to fetch. If the size cannot be determined, the bounded read loop cannot proceed safely.

Source

Thrown at packages/adapter-utils/src/command-managed-runtime.ts:290

          const end = Math.min(total, offset + REMOTE_WRITE_FALLBACK_DECODED_CHUNK_SIZE);
          const chunk = buffer.subarray(offset, end).toString("base64");
          await runShell(`base64 -d >> ${shellQuote(remoteTempPath)}`, { stdin: chunk });
          await options?.onProgress?.(end, total);
        }
        await runShell(`mv -f ${shellQuote(remoteTempPath)} ${shellQuote(remotePath)}`);
        await options?.onProgress?.(total, total);
      } finally {
        await bestEffortRemoveRemotePath(client, remoteTempPath);
      }
    },
    readFile: async (remotePath, options) => {
      // Chunked reads intentionally query the remote size first, even without
      // a progress sink, so each sandbox RPC stays bounded and truncation is
      // detected without materializing the whole file as one stdout string.
      const sizeResult = await runShell(`wc -c < ${shellQuote(remotePath)}`);
      const totalBytes = Number.parseInt(sizeResult.stdout.trim(), 10);
      if (!Number.isFinite(totalBytes) || totalBytes < 0) {
        throw new Error(`Could not determine remote file size for ${remotePath}`);
      }

      // Read in bounded remote chunks so the runner never has to materialize a
      // single base64 stdout string for the whole archive. The client API still
      // returns the decoded file as a Buffer, but every command result stays
      // small enough for provider-backed sandbox RPCs.
      const decodedChunks: Buffer[] = [];
      let decodedSoFar = 0;
      if (totalBytes === 0) {
        await options?.onProgress?.(0, 0);
        return Buffer.alloc(0);
      }
      for (let chunkIndex = 0; decodedSoFar < totalBytes; chunkIndex++) {
        const result = await runShell(
          `dd if=${shellQuote(remotePath)} bs=${REMOTE_READ_CHUNK_BYTES} skip=${chunkIndex} count=1 2>/dev/null | base64`,
        );
        const chunk = Buffer.from(result.stdout.replace(/\s+/g, ""), "base64");
        if (chunk.byteLength === 0) break;

View on GitHub (pinned to 67001ec6eb)

Solutions

  1. Verify the remote path exists and is a regular file: run 'ls -la <remotePath>' or use client.listFiles on the parent directory.
  2. If reading a special file (FIFO, device), read it with a different mechanism (e.g., client.run with cat) instead of readFile.
  3. Check the sandbox's wc implementation: run 'wc -c < /dev/null' to confirm it returns 0.
  4. Handle race conditions by retrying or ensuring the file is not modified/deleted during the read.

Example fix

// before: reading a non-regular file
await client.readFile('/workspace/pipe'); // FIFO -> wc returns unexpected value

// after: check file type first, use run() for special files
const result = await client.run(`file ${shellQuote(remotePath)}`);
// if special file, use cat instead:
const data = await client.run(`cat ${shellQuote(remotePath)} | base64`);
Defensive patterns

Strategy: validation

Validate before calling

async function isRemoteFileReadable(client: SandboxManagedRuntimeClient, remotePath: string): Promise<boolean> {
  try {
    // Verify the path is a regular file and wc works
    const result = await client.run(`test -f ${shellQuote(remotePath)} && wc -c < ${shellQuote(remotePath)}`);
    const size = Number.parseInt(result.stdout.trim(), 10);
    return Number.isFinite(size) && size >= 0;
  } catch {
    return false;
  }
}

// Call before client.readFile:
if (!(await isRemoteFileReadable(client, remotePath))) {
  throw new Error(`Remote path ${remotePath} is not a readable regular file.`);
}

Try / catch

try {
  const data = await client.readFile(remotePath);
} catch (error) {
  if (error instanceof Error && error.message.includes('Could not determine remote file size')) {
    // The file may not exist or may be a special file
    // Check file type and existence, then retry or use an alternative read method
    const fileInfo = await client.run(`file ${shellQuote(remotePath)} 2>&1 || echo 'missing'`);
    console.error('File info:', fileInfo.stdout);
  }
  throw error;
}

Prevention

When it happens

Trigger: Calling client.readFile(remotePath) when the remote 'wc -c' command output cannot be parsed as a non-negative finite integer. This happens when the file does not exist (wc prints an error and the exit code is non-zero, but the error is already caught by requireSuccessfulResult), or when wc produces unexpected output (e.g., locale-dependent formatting, a symlink loop, or a special file like /dev/zero where wc may hang or return unexpected values).

Common situations: Reading from a path that is a FIFO, device file, or other special file where wc -c behaves unexpectedly. A locale or wc implementation that formats output differently. The file was deleted between the size probe and the read. The sandbox environment has a non-standard wc binary.

Related errors


AI-assisted analysis of paperclipai/paperclip@67001ec6eb (2026-08-12). Data as JSON: /api/errors/6cca697f3a7d3e6d. Report an issue: GitHub.