ToolJet/ToolJet · error · GrpcOperationError

Directory does not exist: ${expandedDir}

Error message

Directory does not exist: ${expandedDir}

What it means

Thrown by validateFilesystemAccess when fs.existsSync returns false for the expanded directory. It fires before any proto loading, so no proto parsing has occurred. The path is expanded (~ -> homedir) and resolved before the check, so the message shows the absolute path that was probed.

Source

Thrown at plugins/packages/grpcv2/lib/operations.ts:32

const expandPath = (inputPath: string): string => {
  if (inputPath.startsWith('~/')) {
    return path.join(os.homedir(), inputPath.slice(2));
  } else if (inputPath === '~') {
    return os.homedir();
  }
  return path.resolve(inputPath);
};

const getDefaultProtoDirectory = (): string => {
  return path.join(os.homedir(), 'protos');
};

export const validateFilesystemAccess = (directory: string): string => {
  // Expand ~ to home directory if needed
  const expandedDir = expandPath(directory);

  if (!fs.existsSync(expandedDir)) {
    throw new GrpcOperationError(`Directory does not exist: ${expandedDir}`);
  }

  const stat = fs.statSync(expandedDir);
  if (!stat.isDirectory()) {
    throw new GrpcOperationError(`Path is not a directory: ${expandedDir}`);
  }

  // Basic security check - prevent path traversal
  const resolvedPath = path.resolve(expandedDir);
  if (!resolvedPath.startsWith(process.cwd()) && !resolvedPath.startsWith(os.homedir())) {
    console.warn(`Directory outside project root or home directory: ${resolvedPath}`);
  }

  return expandedDir;
};

export const buildReflectionClient = async (sourceOptions: SourceOptions, serviceName: string): Promise<GrpcClient> => {
  try {

View on GitHub (pinned to 20602a8e10)

Solutions

  1. Create the directory or point proto_files_directory to the real location: mkdir -p <dir>.
  2. Check the expanded path shown in the message — verify leading ~ expansion and cwd resolution.
  3. If you intended the default, ensure ~/protos exists, or set proto_files_directory explicitly.

Example fix

// before
sourceOptions.proto_files_directory = '~/proto';
// after
sourceOptions.proto_files_directory = '~/protos';
// then: mkdir -p ~/protos
Defensive patterns

Strategy: validation

Validate before calling

function ensureDirExists(dir: string): string {
  const fs = require('fs');
  const expanded = dir.startsWith('~/') ? require('path').join(require('os').homedir(), dir.slice(2)) : require('path').resolve(dir);
  if (!fs.existsSync(expanded)) throw new Error(`Proto directory missing: ${expanded}. Create it or fix proto_files_directory.`);
  return expanded;
}

Prevention

When it happens

Trigger: sourceOptions.proto_files_directory points to a path that does not exist on disk; a relative path resolved against an unexpected cwd; a typo in the directory; the protos folder was deleted or never created. The default fallback is os.homedir()/protos, which rarely exists on a fresh machine.

Common situations: Fresh checkout where ~/protos was never created; Docker/CI containers with a different HOME or working volume; config referencing an absolute path from a different developer's machine; symlinks pointing to a removed target.

Related errors


AI-assisted analysis of ToolJet/ToolJet@20602a8e10 (2026-08-13). Data as JSON: /api/errors/3395a572ca3732b8. Report an issue: GitHub.