Skip to main content

Silent Killer: unknown flag or subcommand " "

Some errors are loud.

A segfault is loud. A panic is loud. A stack trace is loud.

You know exactly where to look.


And then there are silent killers.

Not silent because they don’t produce errors. Silent because they produce errors that point you in the wrong direction.

This is one of them:

error: unknown flag or subcommand " "

At first glance, this looks like:

  • your CLI parser is broken
  • your flags are malformed
  • your binary isn’t handling input correctly
  • maybe even something weird with how Bash is invoking the command

It looks like a system problem.

It is not.


The Setup

You’re writing a multi-line command because it’s getting long:

~/gh/fmc/fmc \
-exportJSON ./source.json \
-dir ../docusaurus-prod/technical/languages/oop-and-functional/ \
-urlStartsAfter /home/tjr/gh/trones-noters/docusaurus-prod/

Clean. Readable. Exactly how you should be doing it.

Except… it doesn’t work.

Instead, you get:

error: unknown flag or subcommand " "

The Actual Problem

This line:

-exportJSON ./source.json \ 

Look closely.

There is a space after the backslash.

That’s it. That’s the entire bug.


Why This Breaks

In Bash, the backslash (\) is a line continuation character.

But only if it is:

the last character on the line

This works:

\⏎

This does not:

\ ⏎

That trailing space cancels the continuation.

So instead of one command, Bash interprets this as:

~/gh/fmc/fmc -exportJSON ./source.json " "

Yes — an actual argument that is literally a single space.

Your binary receives:

["-exportJSON", "./source.json", " "]

And your CLI does exactly what it should do:

unknown flag or subcommand " "

The system is correct.

But the error is cognitively misleading.


Why This Is a Silent Killer

This bug is powerful because it breaks multiple assumptions at once:

1. The character is invisible

You cannot see it unless you’re specifically looking for it.


2. The failure happens downstream

The shell misinterprets your command, but the error shows up in your application layer.


3. The error message is technically correct

Your program did receive " " as an argument.

But that is not how your brain models the problem.


4. It sends you searching in the wrong place

You start thinking about:

  • flag parsing
  • CLI frameworks
  • argument ordering
  • quoting issues
  • maybe even rebuilding the binary

None of those are the problem.


First Move: Check for Invisible Characters

When you see an error that:

  • doesn’t match your mental model
  • doesn’t make sense given what you’re looking at
  • or seems to implicate the wrong layer

One of the first things you should do is:

Assume there are characters you are not seeing

Most editors do not render:

  • trailing spaces
  • control characters
  • carriage returns
  • subtle whitespace differences

So what you think you wrote and what the system received can diverge.


CLI: What the Machine Actually Sees

cat -A my_script.sh

This shows:

  • $ → end of line
  • visible trailing spaces
  • control characters

In my case, this made the issue obvious:

  • the backslash was followed by a space
  • then the end of line

Which means the line continuation never happened.

cat -A output


VS Code: What You Thought You Saw

By default, VS Code hides this completely.

Use the quick toggle:

Ctrl + Shift + P
→ Toggle Render Whitespace

Now the editor shows:

  • spaces as dots
  • trailing whitespace clearly
  • alignment differences that actually matter

Which immediately reveals the problem.

VS Code whitespace rendering


The Gap

Here’s the important part:

The bug was not in the logic. The bug was in the representation.

  • VS Code (default): shows intent
  • cat -A: shows reality

And debugging lives in the gap between those two.


The Heuristic

When something feels off:

  1. Don’t assume your logic is wrong
  2. Don’t assume the tool is broken
  3. First check:

“Am I actually passing the characters I think I am?”


The Fix

Remove the trailing space so the backslash is the final character:

~/gh/fmc/fmc \
-exportJSON ./source.json \
-dir ../docusaurus-prod/technical/languages/oop-and-functional/ \
-urlStartsAfter /home/tjr/gh/trones-noters/docusaurus-prod/

That’s it.


A Better Pattern (Optional)

If you want to eliminate this entire class of bugs:

ARGS=(
-exportJSON ./source.json
-dir ../docusaurus-prod/technical/languages/oop-and-functional/
-urlStartsAfter /home/tjr/gh/trones-noters/docusaurus-prod/
)

~/gh/fmc/fmc "${ARGS[@]}"

No backslashes. No continuation rules. No invisible failure modes.


The Real Lesson

This is not about Bash.

This is about where bugs hide.

The most time-consuming bugs are not always:

  • complex algorithms
  • distributed systems issues
  • concurrency problems

Sometimes they are:

low-entropy problems with high misdirection

A single character.

In a place your eyes were never trained to inspect.


Closing Thought

The system didn’t fail.

The system did exactly what you told it to do.

You just didn’t realize what you told it.

And that’s what makes it a silent killer.