Amazon Developer

as

Settings
Sign out
Notifications
Alexa
Amazon Appstore
Ring
AWS
Documentation
Support
Contact Us
My Cases
Get Started
Design and Develop
Publish
Reference
Support
Skip to main content
JavaScript (JS) can run in the background without a user interface (UI) in two ways: headless tasks and headless services. Tasks are one-time operations that run independently, while services allow ongoing interaction. For example, a task might fetch TV channel data and update the program guide without opening the app. Services handle functions like media playback - the UI can send commands (like play or pause) to the service while it manages media streaming and processing in a separate runtime. This approach keeps the main interface responsive by moving heavy processing to the background.

JS runtime for headless JS tasks and services

Headless JS tasks and services utilize Vega App Components in the background. App components add a secondary JS runtime (powered by Hermes) on top to enable JS code execution and allow you to listen to lifecycle events from your code. Each instance of a headless JS task and service has its own JavaScript runtime instance.

Supported modules in headless JS context

To maintain a small memory footprint, the headless runtime is a simplified version of the available user interface and supports only the modules listed in the following table.

Supported React Native libraries in headless JS context

Since the headless JS runtime supports limited modules, it only supports a limited set of React Native libraries, listed in the following table.

Using unsupported modules

Using any module or library not listed above results in runtime errors, like the following example. This error complains about the DeviceInfo module that isn’t available in headless JS context. E Volta:[KeplerScript-JavaScript] Invariant Violation: TurboModuleRegistry.getEnforcing(...): 'DeviceInfo' could not be found. Verify that a module by this name is registered in the native binary., js engine: hermes. This error can appear even when you don’t directly import the problematic module. The error happens when:
  • You use a module that imports the problematic module
  • You try to use an unsupported library
  • You import a module incorrectly
For instance, if you import @amazon-devices/react-native-w3cmedia directly instead of using its headless export when writing code for headless JS, you’ll see this error.
The headless export of @amazon-devices/react-native-w3cmedia only includes modules that work safely in headless JS. If you skip this export, your code will try to load UI-related modules that depend on unsupported features like DeviceInfo from @amazon-devices/react-native-kepler, causing errors. While we plan to add tools that detect unsupported modules during build time, for now, make sure you only use modules that Vega has approved for headless JavaScript.

Build headless JS tasks and services

To register your headless JS tasks and services, you need to do the following:

Step 1: Advertise your components in app’s manifest (manifest.toml)

Variations in manifest files

Different headless JS tasks and services need different settings in manifest.toml file. For example:
  • EPG Sync Task uses "runtime-module = /com.amazon.kepler.headless.runtime.loader_2@IKeplerScript_2_0"
  • Headless JS playback uses "runtime-module = /com.amazon.kepler.keplerscript.runtime.loader_2@IKeplerScript_2_0"
Process management also varies by use case:
  • Headless JS playback runs in the same process as your UI
  • EPG Sync Tasks run in a separate process
Always check the specific documentation for each feature you’re implementing. Don’t copy settings between different headless JS features, as small differences can cause major problems.

Step 2: Register entry points

To register entry points for tasks (using the doTask function) and services (using the onStartService and onStopService functions), we recommend that you create two separate files: task.js and service.js. Place the files alongside index.js, which registers the entry point for the UI using AppRegistry.registerComponent. You can use task.js and service.js files to register entry points for multiple headless JS tasks and services.
When using react-native build-vega to build your app, the process automatically handles headless JS bundles. Place these files in your project root:
  • task.js for headless tasks
  • service.js for headless services
The build system finds these files and creates:
  • task.hermes.bundle for tasks
  • service.hermes.bundle for services
These bundles are automatically included in your app package (VPKG).
If you don’t use react-native build-vega, you’ll need to manually:
  • Generate Hermes bytecode bundles from your entry point files (task.js and service.js)
  • Add these bundles to your app package (VPKG) yourself

Debug headless JS tasks and services

Logging

To debug your code in headless JS tasks/services, you can use console logs (console.log) and see them in device Logs Run the following command from your device or simulator shell:
To limit the logs from getting trimmed, use the following commands from the device or simulator shell:

Quick JS bundle updates without rebuild

Headless JS tasks and services can now fetch the updated JS bundle without having to rebuild and reinstall the app. Headless JS tasks and services don’t support Hot Reload or Fast Refresh, which allows changes to be visible without relaunching. Instead, you need to relaunch headless JS tasks and services to pull the updated JS bundle. To use this feature, follow the steps below.
  1. Build your app (UI and headless JS components) in debug mode. Note: Line-by-line debugging, as with UI components, doesn’t work with release builds.
  2. Install the app.
  3. Run the Metro server (npm start) from the project root.
  4. Set up reverse port forwarding from your device (target) to your development machine (host) for the port that the app and Metro are configured to use (typically 8081). Without this step, the app can’t connect to the Metro server.
  5. Launch the app. You should see that all JS bundles (both UI and Headless JS) are now served by Metro.

Line-by-line Debugging

You can debug headless JS tasks and services line-by-line, but with some limitations:
  • Only works with Vega Studio’s Dev Tools
  • Dev Tools only shows the most recently launched JS component
For example, in the Headless JS Media Playback app:
  • Both UI and service run together
  • The service launches last, so Dev Tools shows service.bundle
  • To debug the UI instead, you must:
    • Launch the headless service first using
    • Then launch the UI component
    • Dev Tools now shows index.bundle
Follow these steps to use line-by-line debugging.
  1. Build your app (UI and headless JS components) in debug mode. Note: Line-by-line debugging, as with UI components, doesn’t work with release builds.
  2. Install the app.
  3. Run the Metro server (npm start) from the project root.
  4. Set up reverse port forwarding from your device (target) to your development machine (host) for port 8081 (or the port configured for your app and Metro). Without this step, the app can’t connect to the Metro server and interact with the Dev Tools.
  5. Launch the Headless JS task or service that you want to debug.
    • Manually launch with this command: .
    • Launch the UI component.
  6. In Vega Studio, open the Command Palette and choose “Vega: Launch Dev Tools”. Navigate to the “Sources” tab and select “React Native”.
  7. Verify that the listed bundle (index.bundle/service.bundle/task.bundle) is the one you want to debug and that its source is loaded. Note: Dev Tools will display an error about failing to load the Source Map - this can be ignored.
  8. To set breakpoints, search for your target code and click on the line number where you want to add the breakpoint. Note that breakpoints will only be hit for code that executes after the breakpoint is set. This means breakpoints in code that executes immediately upon launch (such as doTask for headless JS tasks or onStartService for services) won’t be triggered. For these cases, continue using logs for debugging.
  9. Close the Dev Tools instance and relaunch it between app launches, as Dev Tools won’t refresh automatically.
A screenshot of a script from the React Native tab in Sources.

Crash Reports

Unhandled JS errors from a headless JS task or service cause the app to crash and generate an Aggregated Crash Report (ACR). Currently, this behavior only applies to release builds of headless JS tasks and services. For debug builds of headless JS tasks and services, continue to use Device Logs to detect any unhandled JS exceptions.

Troubleshooting issues with headless JS tasks and services

My headless JS task/services work in debug mode but doesn’t launch or errors out in release mode

Check the logs and if you see an error like the following then your task/service bundle is trying to initialize or use a module that’s not supported in headless JS context.
To identify if that’s the case you can do the following:

Step 1: Use react-native-bundle-visualizer to look at the composition of your headless JS task/service bundle

For example, compare the composition of a task.bundle when it accidentally pulls in the unsupported modules from @amazon-devices/react-native-kepler.
Scenario 1: Correct import
A screenshot that shows the mapping of files in box-like formations with percentages and sizes in KB. Scenario 2: Wrong import
A screenshot of another view of the mapping of files. The composition of the task.bundle in Scenario 2 indicates that the bundle has included numerous modules from @amazon-devices/react-native-kepler, even those that are not supported in a headless JS context. Once you have confirmed that the task/service bundle includes unsupported modules, proceed to step 2 to determine who is responsible for bringing them in.

Step 2: Inspect the debug built task.bundle/service.bundle manually

Open the JS bundle (not the Hermes bytecode bundle) for the service/task (whichever is throwing error). You can typically find them in build/lib/rn-bundles/Debug. Let’s say it is task.bundle. You can open it using any of your favorite editors. For example, let’s take the above task.bundle with the wrong import, and the following error:
You can search for TurboModuleRegistry.getEnforcing call that takes DeviceInfo as the input. Once you find that, you can determine which module has this code. For example, in above task.bundle it is:
This code comes from @amazon-devices/react-native-kepler/Libraries/Utilities/NativeDeviceInfo.js. Next step is to find out who is depending on this module. For that you can search for module ID 247 in task.bundle. In the above task.bundle, there are two modules that depend on module ID 247:
and
You can pick any one of these and search for its consumers. Doing this few more times leads to the following module: node_modules/@amazon-devices/react-native-kepler/index.js
which is found to be imported from task.js
Future releases will automate the detection of offending modules using a tool. This tool could be integrated into the build step itself to warn about unsupported modules and their origin, potentially causing the build to fail.

Step 3: Reach out to your Amazon contact

If you’ve encountered a dead end due to the absence of a suitable tool to detect unsupported modules, Amazon will be more than willing to assist you in diagnosing the issue. Please reach out to your Amazon contact for the same.

Current applications of headless JS tasks and services

Headless JS tasks are employed for the following Fire TV integrations: Headless JS Services are leveraged for the following use cases:
Last modified on December 10, 2025