Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

iTechGuides is reader-supported. When you buy through links on our site, we may earn an affiliate commission. As an Amazon Associate I earn from qualifying purchases. Learn more

systemd is rejecting the command because the first token in ExecStart= does not resolve to an executable in the form that your systemd version accepts. Put a real executable path first—preferably an absolute path such as /usr/local/bin/myapp—then reload the manager and restart the service. Shell variables, shell builtins such as exec, and copied shell-style quoting are common causes of the warning.

What the warning means

ExecStart= is not a shell command line by default. systemd parses it into arguments and treats the first argument as the executable to launch. If that first argument is not accepted as an executable path, systemd reports a message such as Executable path is not absolute and may leave the unit without a usable ExecStart.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

The warning is about command parsing, not yet about whether the application starts successfully. A file can exist, have the wrong permissions, lack its interpreter, or fail to read its configuration after the parser accepts it; those are separate execution-stage problems.

Typical forms that trigger the warning

  • ExecStart=$CELERY_BIN worker puts a variable reference in the executable position. An upstream report for issue #1274 records this pattern producing the same warning.
  • ExecStart=exec /usr/local/bin/myapp uses exec, a shell builtin, as though it were a program. systemd does not invoke a shell to interpret it.
  • ExecStart='/tmp/space bin' can be parsed as an unusable executable token. Upstream issue #2132 records the exact warning: Executable path is not absolute, ignoring: '/tmp/space bin'.

In each case, the repair is to make the first token a path that systemd can resolve, and to invoke a shell explicitly only when shell syntax is actually required.

Use an absolute executable path first

The most portable form is an absolute path with no dependence on the service manager’s search path:

[Service]
ExecStart=/usr/local/bin/myapp --config /etc/myapp/config.yml

Find the installed program from a shell, then verify the path before editing the unit:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
command -v myapp
ls -l /usr/local/bin/myapp

command -v reports the executable found through your current shell’s PATH; ls -l confirms the file at the path you intend to place in the unit. Your interactive PATH is not necessarily the one used by systemd, so copy the actual absolute location rather than relying on the shell result alone.

Repair the unit step by step

  1. Inspect the loaded unit and drop-ins

    First see the complete service definition that systemd is using, including administrator overrides:

    systemctl cat example.service
    systemctl show example.service -p FragmentPath -p DropInPaths

    systemctl cat displays the main unit and any drop-in files in the order systemd loads them. The FragmentPath and DropInPaths properties identify the files you must edit; changing a similarly named copy elsewhere will not fix the loaded service.

  2. Locate and verify the executable

    Resolve the program to a real file and check its mode:

    Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
    command -v myapp
    ls -l /usr/local/bin/myapp

    If the command is a script, inspect its interpreter line and executable permission. A script launched directly needs a valid shebang, such as #!/bin/bash, and execute permission. If either is uncertain, call the interpreter explicitly as shown below.

  3. Put that path in ExecStart=

    Replace the invalid first token with the absolute executable path:

    [Service]
    ExecStart=/usr/local/bin/myapp --config /etc/myapp/config.yml

    Do not prepend exec, source, export, or a variable. Those are shell constructs, not executable files.

  4. Choose the correct form for a script

    Use an explicit interpreter when you want to control which interpreter runs the script:

    Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
    [Service]
    ExecStart=/bin/bash /opt/myapp/start.sh

    Alternatively, make the script directly executable, give it a valid shebang, and use its absolute path:

    [Service]
    ExecStart=/opt/myapp/start.sh

    The direct form depends on the shebang and execute permission. The interpreter form names the interpreter in the unit and does not depend on the script’s execute bit, but it changes which program is the launched process and can affect signal handling.

  5. Handle spaces with systemd syntax

    Unit files are not shell scripts. Do not paste shell quoting into ExecStart= and assume it will be interpreted identically. The upstream space-path report demonstrates that a single-quoted value such as ExecStart='/tmp/space bin' can produce the absolute-path warning.

    Use systemd’s own command-line escaping and quoting rules. For a path containing a space, escape the space in the unit syntax rather than relying on shell-only quoting; for example:

    Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
    [Service]
    ExecStart=/tmp/spacex20bin

    Keep the executable as the first token and test the exact unit after editing. If the program accepts separate arguments, leave those arguments as separate systemd words instead of composing a shell command string.

  6. Reload and test the manager

    After saving the unit or drop-in, reload systemd’s configuration and restart the service:

    systemctl daemon-reload
    systemctl restart example.service
    systemctl status example.service --no-pager
    journalctl -u example.service -b --no-pager

    The reload is required for systemd to parse the edited file. status shows the current state and recent failure reason; the journal shows the service’s start attempt and any application-level error.

Absolute paths, simple names, and the systemd version distinction

Documentation history explains why advice on this warning differs between systems. Older systemd behavior and documentation required an absolute path for the executable. Later behavior permits a simple filename without slashes and resolves it through a fixed search path. An absolute path remains the clearest choice when a unit must work across distributions and systemd releases.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Executable form Portability across systemd versions Path dependency Maintenance trade-off
/usr/local/bin/myapp Highest; satisfies the older absolute-path rule None beyond the path itself Must be edited if software moves
myapp Depends on a version that permits a simple filename Uses systemd’s fixed search path, not your interactive shell’s path Can survive relocations within configured search directories, but may hide which binary is selected
$MYAPP or another variable Unsafe in the executable position Requires shell expansion that systemd does not perform there Replace with a real path or invoke a shell explicitly

Current systemd manuals document ExecSearchPath=, a colon-separated list of absolute directories used to locate executables, and identify the directive as added in systemd version 250. On systems that support it, a unit can declare its search directories explicitly:

[Service]
ExecSearchPath=/usr/local/bin:/usr/bin
ExecStart=myapp

That setting is version-sensitive, so do not add it to a unit that must run on older managers without checking availability. The system manager’s documented fixed path is /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin; it can differ from the path shown by your interactive shell.

When shell syntax is genuinely required

systemd does not expand shell variables, command substitutions, pipelines, redirections, source, or shell builtins in a normal ExecStart= line. If the service really needs that syntax, invoke a shell by its absolute path and pass the command to -c:

[Service]
ExecStart=/bin/sh -c 'command1 --option "$CONFIG" | command2'

This is a deliberate change from direct execution. The shell now performs expansion and parsing, and its quoting rules apply to the command string. The shell process can also change how signals and exit statuses reach the application. Use this form only for behavior that cannot be represented as direct executable arguments; otherwise, call the application directly.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

If you need an environment value, prefer systemd’s environment mechanisms rather than placing a variable in the first token. For example, keep the executable literal and pass the value as an argument, or configure the service environment separately, while retaining a real executable path in ExecStart=.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Direct script execution versus an explicit interpreter

Choice Interpreter selection Permissions and shebang Process and signal behavior
ExecStart=/opt/myapp/start.sh Selected by the script’s shebang Requires execute permission and a valid shebang The script is launched directly; the interpreter relationship follows normal shebang execution
ExecStart=/bin/bash /opt/myapp/start.sh Explicitly selects /bin/bash The script need not be executable, but the file must be readable by the service user bash is the process started by systemd, which can affect signal forwarding and shutdown behavior

Choose direct execution when the script is packaged with the correct shebang and permissions and you want that shebang honored. Choose an explicit interpreter when the unit must enforce a particular interpreter or the script’s execute bit cannot be relied upon.

Diagnose failures that remain after the warning disappears

Once systemd accepts the command, a failure no longer means the executable-path parser rejected it. Work through the remaining checks in the order that matches the symptom:

  • File missing: Confirm the exact path with ls -l. A path that exists in a development shell may not exist in the service’s filesystem or deployment image.
  • Not executable: For direct script execution, check the execute bit and shebang. Calling /bin/bash explicitly avoids dependence on the script’s execute bit.
  • Wrong interpreter: A valid-looking script can fail if its shebang names an interpreter that is absent or if it uses syntax from a different shell. The explicit-interpreter form makes the choice visible in the unit.
  • Configuration or working-directory errors: The service may not be able to read its configuration or working directory. Check the paths referenced by the application and the service’s configured user.
  • Permissions or sandbox restrictions: The service account and any systemd sandbox settings can deny access even when the executable itself is present. Use the status output and journal to identify the denied path or operation.

Use systemctl cat again if the edited line appears to have no effect; a drop-in may override the main unit, or a different unit file may be loaded than the one you changed. Use systemctl status and journalctl -u to separate parser messages from runtime and application messages.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Common mistakes and their corrections

Incorrect line Why it fails Correct approach
ExecStart=$CELERY_BIN worker The first token is a variable reference, not a resolved executable path Write the absolute Celery executable path first, or call a shell explicitly if expansion is unavoidable
ExecStart=exec /usr/local/bin/myapp exec is a shell builtin and systemd is not running a shell Use ExecStart=/usr/local/bin/myapp
ExecStart=' /tmp/space bin ' Shell-style quoting and extra spaces can leave an invalid executable token Use systemd’s escaping rules, keep the path first, and test the loaded unit
ExecStart=myapp on an older manager The manager may require an absolute path Use the full path, or verify support for simple-name resolution and configure ExecSearchPath= where available
Editing a vendor unit while a drop-in overrides it The loaded command remains unchanged Inspect FragmentPath and DropInPaths, then edit the effective override
Editing without reloading systemd continues using its previously parsed configuration Run systemctl daemon-reload before restarting

A compact verification checklist

  • The first ExecStart= token is a real executable, preferably an absolute path.
  • The executable exists at that exact path and is accessible to the service account.
  • A directly launched script has a valid shebang and execute permission.
  • No shell builtin or unexpanded variable occupies the executable position.
  • Paths containing spaces use systemd command-line escaping, not copied shell syntax.
  • Any use of shell syntax calls /bin/sh or another interpreter by absolute path with -c.
  • The unit’s actual fragment and drop-ins have been inspected.
  • systemctl daemon-reload was run after editing.
  • systemctl status and journalctl -u were checked after the restart.

What to remember

The warning identifies an invalid executable token during unit parsing. Make the first ExecStart= token a path to the program you intend to run, use an explicit interpreter for scripts when appropriate, and reserve shell invocation for commands that truly need shell syntax. Absolute paths avoid differences between older and newer systemd behavior and avoid surprises from the manager’s fixed search path. After the parser accepts the line, use the service status and journal to investigate ordinary file, permission, interpreter, configuration, and sandbox failures.