Troubleshooting¶
This page covers the most common problems encountered when starting with Sen, in a symptom → cause → fix format. For a deeper understanding of why things work the way they do, see Understanding Sen: A Mental Model and Working with Objects.
Quick diagnostics checklist¶
Before diving into specific problems, run through this list:
- Is the Sen environment sourced? (
sen --versionshould print a version number) - Is
LD_LIBRARY_PATHset to include your build output? (e.g.$(pwd)/build/bin) - Did CMake run successfully and did
make(or similar) complete without errors? - Does your YAML config reference the correct class names (case-sensitive,
package.ClassName)? - Do all bus names in YAML exactly match what your code uses?
- Does every exported class have
SEN_EXPORT_CLASS(ClassName)at the bottom of its.cpp?
Build and environment¶
Package fails to load at runtime — dlopen error or cannot open shared object file¶
Symptom: Sen starts but immediately prints something like:
Cause: The shared library was built but the runtime linker cannot find it. LD_LIBRARY_PATH
does not include your build output directory.
Fix:
Add this to your shell profile or a project-local setup.sh script so you do not have to run it
every time.
Tip
Run ldd build/bin/libmy_package.so to verify all transitive dependencies are also found.
Generated .stl.h files are missing or stale¶
Symptom: Build fails with #include "stl/my_package/my_class.stl.h": No such file or directory,
or you added a property in your STL file but it does not appear in the C++ class.
Cause: The code generator has not been run since the STL file was created or modified.
Fix: The generator runs automatically as part of the CMake build if you used add_sen_package()
correctly. Run a full clean build:
If the file is still missing, check that your STL files are listed in the STL_FILES argument of
add_sen_package() in your CMakeLists.txt:
add_sen_package(
TARGET my_package
...
STL_FILES
stl/my_package/my_class.stl # ← must be listed here
)
Object discovery¶
Object never appears in the shell / subscription is always empty¶
Symptom: You run ls in the shell and see nothing, or your Subscription<T> list
is always empty even though the component started successfully.
Cause (most common): Bus name mismatch. The object is published to a different bus than the one you are watching.
Fix: Check that the bus name in your YAML config exactly matches what you use in code and in the shell. Bus names are case-sensitive:
# YAML config
objects:
- class: my_package.MyClass
name: obj1
bus: local.my_bus # ← this must exactly match
Object appears, then immediately disappears¶
Symptom: The object briefly shows up but vanishes within a cycle or two.
Cause 1: The object is being destroyed before the component finishes registering — typically because it is a local variable that goes out of scope.
Fix: Objects must be owned by the component (stored as shared_ptr members), not as locals.
Cause 2: Subscription<T> is a local variable. When it goes out of scope, it tears down the
subscription, which can cascade into the object being removed.
Fix: Make Subscription<T> a member variable of your class:
class MyManagerImpl : public MyManagerBase
{
// ✅ correct — member variable, lives as long as the object
sen::Subscription<WorkerInterface> workers_;
// ❌ wrong — local in registered(), destroyed after exiting `registered`.
void registered(sen::kernel::RegistrationApi& api) override
{
sen::Subscription<WorkerInterface> workers;
}
};
Two objects with the same name¶
Symptom: You instantiate two objects but only one appears, or behavior is inconsistent.
Cause: Object names must be unique within a bus. If two objects share a name, one will be rejected.
Fix: Ensure each object has a distinct name in the YAML config:
objects:
- class: my_package.Worker
name: worker1 # unique
bus: local.workers
- class: my_package.Worker
name: worker2 # unique
bus: local.workers
Properties and state¶
Property changes are not visible to other objects¶
Symptom: You update a property in your update() function but other components always see the
old value.
Cause 1: You are assigning to a local variable or calling a non-staging setter, instead of
using setNextProp().
Fix: Always use the generated setNext accessor to stage property changes:
// ✅ correct — stages the change for commit
setNextPosition(newPosition);
// ❌ wrong — this does nothing to the Sen property
position_ = newPosition;
Cause 2: You are calling setNextProp() outside of the update() or drain callback context
(e.g., from a constructor or a non-Sen thread).
Fix: All property changes must happen during the update or drain stage. Initialize values using
the YAML config (prop_name: initial_value) or in registered() via the appropriate API.
Startup crash: "static property has no initial value"¶
Symptom: The kernel crashes or prints an error at startup referencing a [static] property.
Cause: Static properties require an initial value at construction time, and none was provided in the YAML config.
Fix: Add the missing initial value to the object's YAML entry:
objects:
- class: my_package.MyClass
name: obj1
myStaticProp: "required value" # ← add this
bus: local.example
The YAML key is the property name as defined in STL.
Method callback fires much later than expected¶
Symptom: The result arrives several cycles after the call, not on the very next one.
Cause: This is normal behavior under load. If the target component is running at a low frequency (e.g. 2 Hz) and the call is made just after its drain, the next drain is almost a full period away.
Fix: Increase the component frequency if lower latency is required, or accept the asynchronous nature of the system. This is not a bug.
Exception from a method is silently ignored¶
Symptom: Your method implementation throws, but the caller never sees the error.
Cause: The MethodResult<R> returned to the callback contains the exception, but the callback
does not check for it.
Fix: Always check isOk() before calling get():
calc.divide(10.0, 0.0, senImplAsyncCall([](sen::MethodResult<double> r) {
if (r.isOk())
{
std::cout << "result: " << r.getValue() << std::endl;
}
else
{
try
{
std::rethrow_exception(r.getError());
}
catch (const std::exception& e)
{
std::cout << "error: " << e.what() << std::endl;
}
}
}));
Configuration¶
Component starts before its dependency is ready¶
Symptom: A component tries to find objects from another component but finds nothing on the first few cycles. Or the kernel logs errors about missing objects during initialization.
Cause: Both components are in the same group, so their startup order is not guaranteed. Or the dependent component is in a lower group number than its dependency.
Fix: Put dependencies in lower group numbers. Higher groups start after all lower groups are fully running:
build:
- name: my_data_source
group: 2 # starts first
- name: my_consumer_component
group: 3 # starts after group 2 is fully up
See Using component groups for the full rules.
Class not found by kernel — "unknown class" error¶
Symptom: The kernel prints something like unknown class: my_package.MyClassImpl at startup.
Cause 1: SEN_EXPORT_CLASS(MyClassImpl) is missing from the .cpp file.
Fix: Add the macro at the bottom of the implementation file:
Cause 2: The package is not listed in the imports of the component that tries to instantiate
the class.
Fix: Add it to the imports list in your YAML config (see the discovery section above).
Cause 3: The class name in YAML uses the wrong case or wrong separator. The format is always
package_name.ClassName — the package name from your STL package declaration, dot, then the
C++ class name.
YAML config error: unexpected key or wrong type¶
Symptom: The kernel refuses to start with a YAML parse error or a type mismatch.
Cause: A property name is misspelled, or the value type does not match the STL declaration (e.g., a string where a number is expected).
Fix: Check that:
- The property key in YAML matches the STL property name exactly (case-sensitive)
- Numeric properties use numbers in YAML, not quoted strings
- [static] properties that are required must be present
Networking¶
Objects not discovered across processes¶
Symptom: You run two sen processes on the same machine (or different machines) but their
objects do not see each other.
Cause: The ether component (Ethernet transport) is not loaded in one or both processes.
Fix: Add the ether component to both process configs and configure matching addresses. See
the Ether component documentation for the full configuration reference.
WSL2 networking issues¶
Multi-process or multi-machine discovery can fail on WSL2 due to how it handles multicast. See the FAQ for WSL2-specific networking guidance.