Error: Not a valid filename for a flow deploying tedge flow

Hi,
I have a new that should trigger every:
interval = "${params.cleanup_interval}"

When I copy the scripts/ flow to /etc/tedge/mappers/c8y/flows/log-cleanup I get the following error:

tedge-mapper-c8y | 2026-05-04T13:10:52.68905296Z  INFO flows: Loading flow: /data/tedge/mappers/c8y/flows/log-cleanup/flow.toml
tedge-mapper-c8y | 2026-05-04T13:10:52.689761909Z ERROR flows: Failed to compile flow /data/tedge/mappers/c8y/flows/log-cleanup/flow.toml: Not a valid filename for a flow
flow.toml
# Run `find` at a regular interval to delete outdated workflow log files.
# The command produces no stdout output, so the JS step simply drops any
# unexpected lines and nothing is published to MQTT.
[input.process]
command = "find /data/tedge/logs/agent/ -type f -mtime +${params.max_age_days} -delete"
interval = "${params.cleanup_interval}"

[[steps]]
script = "main.js"
// Drop all output from the find command.
// find ... -delete produces no stdout, but any unexpected lines are silently
// discarded here so nothing is published to MQTT.
export function onMessage(_message, _context) {
    return [];
}

export function onInterval(_message, _context) {
    return [];
}

I copy the flow to tedge container bundle, thin-edge 2.0.0.

What do I have to change?

Regards Christof

I note that the errors messages are related to files in /data/tedge and not /etc/tedge.

  • Do you have a symlink from /etc/tedge to /data/tedge?
  • This might confuse tedge flows when deriving the name of flow from its file path.

I start the container with:

docker rm -f tedge 2>/dev/null; docker run -d \
    --name tedge \
    --add-host host.docker.internal:host-gateway \
    --network tedge \
    -p "127.0.0.1:1883:1883" \
    -p "127.0.0.1:8000:8000" \
    -p "127.0.0.1:8001:8001" \
    -v device-certs:/etc/tedge/device-certs \
    -v tedge:/data/tedge \
    -v /var/run/docker.sock:/var/run/docker.sock:rw \
    -e CA=c8y \
    -e TEDGE_C8Y_OPERATIONS_AUTO_LOG_UPLOAD=always \
    -e "DEVICE_ID=${DEVICE_ID}" \
    -e "DEVICE_ONE_TIME_PASSWORD=${DEVICE_ONE_TIME_PASSWORD}" \
    -e "TEDGE_C8Y_URL=${TEDGE_C8Y_URL}" \
    ghcr.io/thin-edge/tedge-container-bundle:latest

What is the output of tedge flows list --mapper c8y ?

0d75ee0e605e:/# tedge flows list --mapper c8y
Flow        : units (v2.0.0)
Description : none
File        : /data/tedge/mappers/c8y/flows/units.toml
Step 1      : update-context

Flow        : alarms (v2.0.0)
Description : none
File        : /data/tedge/mappers/c8y/flows/alarms.toml
Step 1      : add-timestamp
Step 2      : cache-early-messages
Step 3      : into-c8y-alarms
Step 4      : limit-payload-size

Flow        : health (v2.0.0)
Description : none
File        : /data/tedge/mappers/c8y/flows/health.toml
Step 1      : cache-early-messages
Step 2      : into-c8y-health-status

Flow        : events (v2.0.0)
Description : none
File        : /data/tedge/mappers/c8y/flows/events.toml
Step 1      : add-timestamp
Step 2      : cache-early-messages
Step 3      : into-c8y-events

Flow        : log-cleanup (unversioned)
Description : none
File        : /data/tedge/mappers/c8y/flows/log-cleanup/flow.toml
Step 1      : /data/tedge/mappers/c8y/flows/log-cleanup/main.js

Flow        : measurements (v2.0.0)
Description : none
File        : /data/tedge/mappers/c8y/flows/measurements.toml
Step 1      : add-timestamp
Step 2      : cache-early-messages
Step 3      : into-c8y-measurements
Step 4      : limit-payload-size

So your flow is actually properly installed. Any error reported by the mapper is also be reported by tedge flows list.

I think the flow should not discard all the messages but emits events telling what files have been deleted (events or messages on a non thin-edge topics if you don’t want to send theses messages to Cumulocity). Doing so would help you to monitor and debug your flow.

Just to give a bit more context to error message you noticed. Thin-edge derives the names of a flow from the path of its definition file, checking first that this files sits in the mapper flows directory. e.g. A flow defined by /etc/tedge/mappers/c8y/flows/log-cleanup/flow.toml is given the name log-cleanup , once removed the c8y flows directory prefix and the uninformative suffix.

I might miss something, but thin-edge properly derives the correct name for your flow despite the symlink from /etc/tedge to /data/tedge. I.e symlinks are properly followed before deriving the name of a flow from its path. I doubled checked, and failed to reproduce your issue.

@Christof_Strack Do you still observe the error?

I just doubled checked what the tedge-container-bundle is doing, and it is using a symlink but not on the /etc/tedge folder, instead /etc/tedge/mappers, e.g.

$ ls -l /etc/tedge/mappers
lrwxrwxrwx    1 tedge    tedge           19 Apr 24 11:06 /etc/tedge/mappers -> /data/tedge/mappers

Thanks for the precision. I doubled checked with that settings and observed no errors.

Unfortunately, something is actually not correct when running inside the tedge container bundle. The error message is emitted for all the flows, not only your flow. However, despite the error message, the flows seem to be working as expected. => I have to investigate.

So there is actually a bug. Hot reloading of flows is not working when the flow directory is actually a symlink. => I will publish a fix.

Meantime, the workaround is to restart the mapper each time a flow is updated.

Updated: The fixed has been merged fix: hot reloading of flows over symlinks by didier-wenzek · Pull Request #4155 · thin-edge/thin-edge.io · GitHub
We still have to plan a release.