Improve error message when passing a non-existent file to clickhouse

When running `clickhouse path/to/nonexistent.sql` or
`clickhouse local /tmp/aaa.rep`, the error messages were confusing:
the main binary showed the generic "Use one of the following commands"
help, and `clickhouse-local` showed "Positional option is not supported".

Now, if the argument looks like a file path (contains `/` or `.`) but
the file doesn't exist, a clear "no such file" error is shown instead.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
This commit is contained in:
Raúl Marín
2026-02-26 11:15:28 +01:00
co-authored by Claude Opus 4.6
parent 2ed123be11
commit 66aeef7f59
4 changed files with 67 additions and 0 deletions
+20
View File
@@ -400,6 +400,26 @@ int main(int argc_, char ** argv_)
main_func = mainEntryClickHouseLocal;
}
/// If the argument looks like a file path but doesn't exist, provide a helpful error
/// instead of the generic "Use one of the following commands" message.
/// The check above routes existing files to clickhouse-local, but when the file
/// doesn't exist, we fall through to `printHelpOnError` which is confusing:
/// $ clickhouse tests/queries/0_stateless/my_test.sql
/// Use one of the following commands: ...
/// We detect file-like arguments by the presence of `/` (path separator)
/// or `.` (file extension), which distinguishes them from mistyped subcommand
/// names like "clickhouse sever" where the generic help is appropriate.
if (main_func == printHelpOnError && argv.size() >= 2)
{
std::string_view arg(argv[1]);
if (arg.contains('/') || arg.contains('.'))
{
std::cerr << "Error: no such file: " << arg << std::endl;
std::cerr << "If you intended to run a script, please check the path." << std::endl;
return 1;
}
}
int exit_code = main_func(static_cast<int>(argv.size()), argv.data());
#if defined(SANITIZE_COVERAGE)
@@ -71,6 +71,13 @@ void ClientApplicationBase::parseAndCheckOptions(OptionsDescription & options_de
option = "query";
else if (std::filesystem::is_regular_file(std::filesystem::path{token}, ec))
option = "queries-file";
else if (token.contains('/') || token.contains('.'))
/// The argument looks like a file path (contains `/` or `.`) but doesn't exist on disk.
/// Give a clear "no such file" error rather than the generic "positional option is not supported"
/// which is confusing when the user meant to pass a file, e.g.:
/// $ clickhouse local /tmp/aaa.rep
/// Positional option `/tmp/aaa.rep` is not supported.
throw Exception(ErrorCodes::BAD_ARGUMENTS, "No such file: {}", token);
else
throw Exception(ErrorCodes::BAD_ARGUMENTS, "Positional option `{}` is not supported.", token);
@@ -0,0 +1,11 @@
Error: no such file: /tmp/nonexistent_04001.sql
If you intended to run a script, please check the path.
exit: 1
Error: no such file: nonexistent_04001.sql
If you intended to run a script, please check the path.
exit: 1
No such file: /tmp/nonexistent_04001.sql
No such file: nonexistent_04001.rep
Use one of the following commands:
OK_04001
OK_04001
@@ -0,0 +1,29 @@
#!/usr/bin/env bash
CUR_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
# shellcheck source=../shell_config.sh
. "$CUR_DIR"/../shell_config.sh
# When the argument to the main binary looks like a file path but doesn't exist,
# the error message should say "no such file" instead of showing the generic help.
# Main binary with non-existent path containing a slash:
${CLICKHOUSE_BINARY} /tmp/nonexistent_04001.sql 2>&1
echo "exit: $?"
# Main binary with non-existent path containing a dot:
${CLICKHOUSE_BINARY} nonexistent_04001.sql 2>&1
echo "exit: $?"
# clickhouse-local with non-existent file path:
${CLICKHOUSE_LOCAL} /tmp/nonexistent_04001.sql 2>&1 | grep -o 'No such file: /tmp/nonexistent_04001.sql'
${CLICKHOUSE_LOCAL} nonexistent_04001.rep 2>&1 | grep -o 'No such file: nonexistent_04001.rep'
# A bare word without dots or slashes should still show the generic help:
${CLICKHOUSE_BINARY} nonexistent_command 2>&1 | grep -F 'Use one of the following commands'
# Existing file should still work (routed to clickhouse-local):
echo "SELECT 'OK_04001'" > /tmp/clickhouse_test_04001.sql
${CLICKHOUSE_BINARY} /tmp/clickhouse_test_04001.sql 2>&1
${CLICKHOUSE_LOCAL} /tmp/clickhouse_test_04001.sql 2>&1
rm -f /tmp/clickhouse_test_04001.sql