Before you continue
Before submitting a bug report, please review our troubleshooting documentation at Troubleshoot Issues | Vega Troubleshooting
If you still want to file a bug report, please make sure to fill in all the details below and provide the necessary information.
NOTE: PLEASE ONLY REPORT A SINGLE BUG USING THIS TEMPLATE.
If you’re experiencing multiple issues, please file a separate report for each.
Bug Description
1. Summary
{“context”: “CHANNEL_ADD_FAILED”, “deviceInfo”: {“appVersion”: “1.1”, “osVersion”: “1.1”, “platform”: “amazonfire_tv_kepler”, “timestamp”: “2025-10-18T06:32:20.301Z”}, “error”: {“message”: “There was an internal error.”, “name”: “Error”, “stack”: "Error: There was an internal error.
Package.json: Added these dependencies
“@amazon-devices/kepler-epg-provider”: “^1.9.0”,
“@amazon-devices/kepler-epg-sync-scheduler”: “^1.2.0”
“@amzn/headless-task-manager”: “^1.0.0”,
manifest.toml:
Copyright (c) 2022 Amazon.com, Inc. or its affiliates. All rights reserved.
PROPRIETARY/CONFIDENTIAL. USE IS SUBJECT TO LICENSE TERMS.
schema-version = 1
[package]
id = “com.zee5.amazon”
title = “Zee5”
version = “1.0.0”
icon = “@image/logo.png”
The [components] section declares the application components in the package, including interactive, service, and task components.
[components]
[[components.interactive]]
id = “com.zee5.amazon.main”
runtime-module = “/com.amazon.kepler.keplerscript.runtime.loader_2@IKeplerScript_2_0”
categories = [“com.amazon.category.main”,“com.amazon.category.kepler.media”]
launch-type = “singleton”
[[components.service]]
id = “com.zee5.amazon.interface.provider”
runtime-module = “/com.amazon.kepler.headless.runtime.loader_2@IKeplerScript_2_0”
launch-type = “singleton”
[[components.task]]
Define the EPG ingestion task that needs to run periodically
id = “com.zee5.amazon.epgSyncTask”
runtime-module = “/com.amazon.kepler.headless.runtime.loader_2@IKeplerScript_2_0”
[[components.task]]
On Install/Update Task
id = “com.zee5.amazon.onInstallOrUpdateTask”
runtime-module = “/com.amazon.kepler.headless.runtime.loader_2@IKeplerScript_2_0”
headless = true
onInstallOrUpdateTask = true
The [processes] section describes which components in the package should run within a shared process when launched. This section is optional.
[processes]
[[processes.group]]
component-ids = [“com.zee5.amazon.main”]
[[processes.group]]
component-ids = [“com.zee5.amazon.interface.provider”]
[[processes.group]]
component-ids = [“com.zee5.amazon.epgSyncTask”]
[[processes.group]]
component-ids = [“com.zee5.amazon.onInstallOrUpdateTask”]
The [wants] section declares the capabilities that are optional for the application to function on a Kepler device. Handle graceful fallbacks in your application code if a feature in this list is unavailable on a device.
[wants]
[[wants.service]]
id = “com.amazon.inputmethod.service”
[[wants.service]]
id = “com.amazon.kepler.pcon.service.main”
[[wants.service]]
id = “com.amazon.drm.key” # Required for DRM video playback
[[wants.service]]
id = “com.amazon.drm.crypto” # Required for DRM video playback
[[wants.service]]
id = “com.amazon.iap.tester.service” # Required for IAP functionality
[[wants.service]]
id = “com.amazon.iap.core.service” # Required for IAP functionality
[[wants.module]]
id = “/com.amazon.iap.core@IIAPCoreUI”
[[wants.module]]
id = “/com.amazonappstore.iap.tester@IIAPTesterUI”
[[wants.service]]
id = “com.amazon.tv.developer.dataservice”
[[wants.privilege]]
id = “com.amazon.tv.subscription-entitlement.privilege.provide-data”
[[wants.privilege]]
id = “com.amazon.tv.content-personalization.privilege.provide-data”
[[wants.service]]
id = “com.amazon.media.server”
[[wants.service]]
id = “com.amazon.mediametrics.service”
[[wants.service]]
id = “com.amazon.inputd.service”
[[wants.service]]
id = “com.amazon.media.playersession.service”
[[wants.service]]
id = “com.amazon.mediabuffer.service”
[[wants.service]]
id = “com.amazon.mediatransform.service”
[[wants.privilege]]
id = “com.amazon.devconf.privilege.accessibility”
[[wants.service]]
id = “com.amazon.gipc.uuid.*”
[[wants.service]]
id = “com.amazon.audio.stream”
[[wants.service]]
id = “com.amazon.audio.control”
[[wants.service]]
id = “com.amazon.audio.system”
[[wants.service]]
id = “com.amazon.network.service”
The [needs] section declares the system capabilities that the application requires to function on a Kepler device. This information helps Kepler package manager and Amazon Appstore to ensure the application is installed on the supported devices. Carefully declare the application’s needs for your desired device targeting. You can choose a combination of needs and wants to maximize your device reach, while providing enhanced features on applicable devices.
[needs]
[[needs.privilege]]
id = “com.amazon.system.audio”
[[needs.privilege]]
id = “com.amazon.media.secureplayback”
[[needs.privilege]]
id = “com.amazon.privilege.security.file-sharing”
[[needs.privilege]]
id = “com.amazon.background-tasks.privilege.run”
[[needs.privilege]]
id = “com.amazon.network.privilege.net-info”
[[needs.privilege]]
id = “com.amazon.kepler.parental_controls.gates.restricted-content-playback”
[[needs.privilege]]
id = “com.amazon.graphics.privilege.display.manager”
Define the privilege your app needs in order to use the EPG Provider interface
[[needs.privilege]]
id = “com.amazon.kepler.tv.privilege.data_provider”
The [tasks] section declares a small routine that can run at specific events during the lifecycle of the application package. At this time the Kepler system supports the install event for running such tasks. This task can be used to set up other tasks before the application’s first launch. The task section is optional.
[tasks]
[[tasks.work]]
component-id = “com.zee5.amazon.onInstallOrUpdateTask”
mode = “install”
The [offers] section declares the capabilities that an application package offers to other application packages installed on a Kepler system. Offers is an optional section.
[offers]
[[offers.service]]
id = “com.amazon.gipc.uuid.*”
[[offers.interaction]]
id = “com.zee5.amazon.main”
[[offers.service]]
id = “com.zee5.amazon.interface.provider”
required-privileges = [“com.amazon.multimedia.privilege.session.manage”]
[[offers.module]]
id = “/com.zee5.amazon.module@ISomeUri1”
includes-messages = [“pkg://com.zee5.amazon.main”]
The [[message]] section defines the URIs that the application package introduces to the rest of the system.
[[message]]
uri = “pkg://com.zee5.amazon.main”
Match the privileges used in [[offers.interaction]]. If privileges are not added, then use “*”.
sender-privileges = [“*”]
receiver-privileges = [“self”]
[[extras]]
key = “interface.provider”
component-id = “com.zee5.amazon.main”
[extras.value.application]
[[extras.value.application.interface]]
interface_name = “com.amazon.kepler.media.IContentLauncherServer”
attribute_options = [“partner-id”]
static-values = { partner-id = “Z5XGL_ZE” }
[[extras.value.application.interface]]
interface_name = “com.amazon.kepler.media.IAccountLoginServer”
attribute_options = [“Status”]
override_attribute_component = { Status = “com.zee5.amazon.interface.provider” }
Below IMediaPlaybackServer entry is needed for KMC integration. Needs to be added for Content Launcher to work.
[[extras.value.application.interface]]
interface_name = “com.amazon.kepler.media.IMediaPlaybackServer”
command_options = [
“Play”,
“Pause”,
“StartOver”,
“Previous”,
“Next”,
“SkipForward”,
“SkipBackward”,
]
attribute_options = [“AudioAdvanceMuted”]
features = [“AdvancedSeek”, “VariableSpeed”, “AudioTracks”, “TextTracks”]
[[needs.module]]
id = “/com.amazon.kepler.media.@IAccountLogin1”
[[needs.module]]
id = “/com.amazon.kepler.media@IContentLauncher1”
[[needs.module]]
id = “/com.amazon.kepler.appstore.iap.purchase.core@IAppstoreIAPPurchaseCoreService”
**
**
Bug Severity
Select one that applies
-
Impacts operation of app
-
Blocks current development
-
Improvement suggestion
-
Issue with documentation (If selected, please share the doc link and describe the issue)
-
Other
-
Blocks current development
2. Steps to Reproduce
Adding mock data to test EPGWill share all the code files
3. Observed Behavior
Explain what actually happened, noting any discrepancies or malfunctions.
I am calling scheduleEPG from App.tsx
useEffect(() => {
scheduleEPG();
}, []);
4. Expected Behavior
Describe what you expected the SDK to do under normal operation.
<!-- Answer here -->
4.a Possible Root Cause & Temporary Workaround
Fill out anything you have tried. If you don’t know, N/A is acceptable
<!-- Answer here -->
5. Logs or crash report
{“context”: “CHANNEL_ADD_FAILED”, “deviceInfo”: {“appVersion”: “1.1”, “osVersion”: “1.1”, “platform”: “amazonfire_tv_kepler”, “timestamp”: “2025-10-18T06:32:20.301Z”}, “error”: {“message”: “There was an internal error.”, “name”: “Error”, “stack”: "Error: There was an internal error.
(Please make sure to provide relevant logs as attachment)
For crash issues, please refer this guide for faster troubleshooting: Detect Where the App Crash Originates | Design and Develop Vega Apps
-
For issues with Kepler Studio Extension, please share log files from below folders:
~/.vscode/extensions/amazon.kepler-extension-<version>/ExtensionLogs ~/.vscode/extensions/amazon.kepler-ui-extension-<version>/ExtensionLogs
6. Environment
Please fill out the fields related to your bug below:
-
SDK Version: Output of
kepler --version -
App State:
Foreground -
OS Information
Please ssh into the device viakepler exec vda shelland copy the output fromcat /etc/os-releaseinto the answer section below. Note, if you don’t have a simulator running or device attachedkepler exec vda shellwill respond withvda: no devices/emulators foundNAME="OS" OE_VERSION="4.0.0" OS_MAJOR_VERSION="1" OS_MINOR_VERSION="1" RELEASE_ID="10" OS_VERSION="1.1" BRANCH_CODE="TV Ship" BUILD_DESC="OS 1.1 (TV Ship/4434)" BUILD_FINGERPRINT="4.0.163721.0(3072cab629675a74)/4434N:user-external/release-keys" BUILD_VARIANT="user-external" BUILD_TAGS="release-keys" BUILD_DATE="Thu Sep 25 15:30:34 UTC 2025" BUILD_TIMESTAMP="1758814234" VERSION_NUMBER="1001010443450" callie:/$
7. Example Code Snippet / Screenshots / Screengrabs
Include any relevant code or component setup in React Native that can help reproduce the bug.
App.tsx
useEffect(() => {
scheduleEPG();
}, []);
// EpgScheduler.ts
import {EpgSyncTaskScheduler} from '@amazon-devices/kepler-epg-sync-scheduler';
import {doTask} from './EpgSyncTask';
export const scheduleEPG = async () => {
try {
await EpgSyncTaskScheduler.scheduleTask(
'com.zee5.amazon.epgSyncTask',
1440,
);
console.info('EPG Sync Task scheduled successfully');
doTask();
} catch (error) {
console.error('Error scheduling EPG Sync Task', error);
}
};
// EpgTaskHandler.ts
import {EpgDataService} from './EpgDataService';
import {EpgSyncResult} from './kepler';
import {
fetchChannelLineup,
fetchProgramLineup,
fetchLiveEventLineup,
} from './mockBackend';
export const doTask = async (): Promise<EpgSyncResult> => {
console.info('🚀 EPG Sync Task Started');
const result: EpgSyncResult = {
success: false,
channelsProcessed: 0,
programsProcessed: 0,
liveEventsProcessed: 0,
};
try {
// ------------------ Channels ------------------
console.info('📡 Fetching channels...');
const channels = await fetchChannelLineup();
console.info(`📡 Retrieved ${channels.length} channels`);
// Log first channel structure for debugging
if (channels.length > 0) {
console.debug('First channel structure:', {
descriptor: channels[0].channelDescriptor,
metadata: channels[0].channelMetadata,
});
}
await EpgDataService.addChannels(channels);
result.channelsProcessed = channels.length;
console.info('✅ Channels processed successfully');
// ------------------ Programs ------------------
console.info('📺 Fetching programs...');
const programs = await fetchProgramLineup();
console.info(`📺 Retrieved ${programs.length} programs`);
await EpgDataService.upsertPrograms(programs);
result.programsProcessed = programs.length;
console.info('✅ Programs processed successfully');
// ------------------ Live Events ------------------
console.info('🎯 Fetching live events...');
const liveEvents = await fetchLiveEventLineup();
console.info(`🎯 Retrieved ${liveEvents.length} live events`);
await EpgDataService.addLiveEvents(liveEvents);
result.liveEventsProcessed = liveEvents.length;
console.info('✅ Live events processed successfully');
result.success = true;
console.info('🎉 EPG Sync completed successfully!');
} catch (error) {
console.error('❌ EPG Sync failed:', error);
result.error =
error instanceof Error ? error.message : 'Unknown error occurred';
result.success = false;
// Enhanced error logging
if (error instanceof Error) {
console.error('Error stack:', error.stack);
}
}
return result;
};
export interface EpgSyncResult {
success: boolean;
channelsProcessed: number;
programsProcessed: number;
liveEventsProcessed: number;
error?: string;
}
// EpgDataService.ts
import {
ChannelLineupProvider,
ProgramLineupProvider2,
LiveEventProvider,
IChannelInfo,
IProgram,
ILiveEvent,
} from '@amazon-devices/kepler-epg-provider';
import {ErrorReporter} from './ErrorReporter';
export class EpgDataService {
static async addChannels(channels: IChannelInfo[]): Promise<void> {
if (!channels || !Array.isArray(channels)) {
await ErrorReporter.reportError(
'Channels data is invalid',
'CHANNEL_VALIDATION',
);
throw new Error('Invalid channels data: channels is not an array');
}
if (channels.length === 0) {
await ErrorReporter.reportError(
'Channels array is empty',
'CHANNEL_VALIDATION',
);
throw new Error('Invalid channels data: channels array is empty');
}
console.info(`Adding ${channels.length} channels to ChannelLineupProvider`);
try {
console.info('Calling ChannelLineupProvider.add()...');
await ChannelLineupProvider.add(channels);
console.info('ChannelLineupProvider.add() completed successfully');
const version = `v${Date.now()}`;
console.info(
`Calling ChannelLineupProvider.commit() with version: ${version}`,
);
await ChannelLineupProvider.commit(version);
console.info('ChannelLineupProvider.commit() completed successfully');
} catch (error) {
console.error('ChannelLineupProvider operation failed:', error);
// Report the specific error
await ErrorReporter.reportError(error, 'CHANNEL_ADD_FAILED');
// Check for specific error types
if (error instanceof Error) {
if (error.message.includes('internal error')) {
await ErrorReporter.handleInvalidChannelsError(error);
}
}
throw new Error(
`Failed to add channels: ${
error instanceof Error ? error.message : 'Unknown error'
}`,
);
}
}
static async upsertPrograms(programs: IProgram[]): Promise<void> {
if (!programs || !Array.isArray(programs)) {
await ErrorReporter.reportError(
'Programs data is invalid',
'PROGRAM_VALIDATION',
);
throw new Error('Invalid programs data');
}
console.info(
`Upserting ${programs.length} programs to ProgramLineupProvider2`,
);
try {
await ProgramLineupProvider2.upsert(programs);
await ProgramLineupProvider2.commit(`program_v${Date.now()}`);
console.info('Programs upserted successfully');
} catch (error) {
console.error('ProgramLineupProvider2 operation failed:', error);
await ErrorReporter.reportError(error, 'PROGRAM_UPSERT_FAILED');
throw error;
}
}
static async addLiveEvents(liveEvents: ILiveEvent[]): Promise<void> {
if (!liveEvents || !Array.isArray(liveEvents)) {
await ErrorReporter.reportError(
'Live events data is invalid',
'LIVE_EVENT_VALIDATION',
);
throw new Error('Invalid live events data');
}
console.info(
`Adding ${liveEvents.length} live events to LiveEventProvider`,
);
try {
await LiveEventProvider.add(liveEvents);
await LiveEventProvider.commit(`live_v${Date.now()}`);
console.info('Live events added successfully');
} catch (error) {
console.error('LiveEventProvider operation failed:', error);
await ErrorReporter.reportError(error, 'LIVE_EVENT_ADD_FAILED');
throw error;
}
}
}
// ErrorReporter.ts
import {
InvalidArgumentError,
StorageLimitError,
IUpsertProgramFailure,
} from '@amazon-devices/kepler-epg-provider';
import {getAppVersion} from '../../utils/app';
import Device from '../../utils/Device';
import DeviceInfo from '@amzn/react-native-device-info';
export class ErrorReporter {
// Report general errors to backend
static async reportError(error: any, context: string): Promise<void> {
const errorReport = {
timestamp: new Date().toISOString(),
context,
error:
error instanceof Error
? {
name: error.name,
message: error.message,
stack: error.stack,
}
: error,
deviceInfo: await this.getDeviceInfo(),
};
console.error('🚨 EPG Error:', errorReport);
// Send to your monitoring system
try {
await this.sendToBackend(errorReport);
} catch (reportingError) {
console.error('Failed to report error to backend:', reportingError);
}
}
// Handle invalid channels error (contains details about failed channels)
static async handleInvalidChannelsError(
error: InvalidArgumentError,
): Promise<void> {
console.error('❌ Invalid channels detected:', error.message);
const errorData = {
timestamp: new Date().toISOString(),
errorType: 'INVALID_CHANNELS',
message: error.message,
// Error message contains details about first 5 failed channels
};
await this.reportError(errorData, 'CHANNEL_VALIDATION');
}
// Handle storage limit error (critical - requires immediate attention)
static async handleStorageLimitError(
error: StorageLimitError,
): Promise<never> {
console.error('💾 STORAGE_LIMIT_EXCEEDED:', error);
const criticalError = {
timestamp: new Date().toISOString(),
errorType: 'STORAGE_LIMIT',
message: 'EPG storage limit exceeded - requires immediate attention',
};
await this.reportError(criticalError, 'STORAGE_LIMIT');
// TODO: Trigger urgent alarm to operations team
// TODO: Contact Amazon support as required
throw error;
}
// Handle program upsert failures
static async handleProgramFailures(
failures: IUpsertProgramFailure[],
batchInfo: string,
): Promise<void> {
if (failures.length === 0) return;
const failureReport = {
timestamp: new Date().toISOString(),
type: 'PROGRAM_UPSERT_FAILURES',
totalFailures: failures.length,
batchInfo,
failures: failures.slice(0, 5).map((failure) => ({
programId: failure.program.identifier,
channelId: failure.program.channelDescriptor.identifier,
error: failure.error.message || 'Unknown error',
})),
};
await this.reportError(failureReport, 'PROGRAM_INGESTION_FAILURES');
}
private static async sendToBackend(errorData: any): Promise<void> {
// Implement your error reporting endpoint
try {
await fetch('https://api.zee5.com/monitoring/epg-errors', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify(errorData),
});
} catch (error) {
console.error('Failed to send error to backend:', error);
}
}
private static async getDeviceInfo(): Promise<any> {
return {
appVersion: getAppVersion(),
platform: Device.getPlatformNameForAPI(),
osVersion: DeviceInfo.getSystemVersion(),
timestamp: new Date().toISOString(),
};
}
}
// mockBackend.ts
import {
IChannelInfo,
IProgram,
ILiveEvent,
ChannelDescriptorBuilder,
ChannelInfoBuilder,
ChannelMetadataBuilder,
ProgramBuilder,
LiveEventBuilder,
EventType,
PlaybackReferenceBuilder,
PlaybackType,
ChannelTypeEnum,
} from '@amazon-devices/kepler-epg-provider';
import {MediaId} from '@amazon-devices/kepler-media-types';
const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
// ------------------ Channels ------------------
export const fetchChannelLineup = async (): Promise<IChannelInfo[]> => {
await delay(1000);
const channels: IChannelInfo[] = [];
// Channel data without logo URLs - you need to add actual working URLs from your CDN
const channelData = [
{
id: 1,
name: 'ZEE TV',
logo: 'https://akamaividz2.zee5.com/image/upload/frontend/branding/images/version-2/logo.png',
},
{
id: 2,
name: '&TV',
logo: 'https://akamaividz2.zee5.com/image/upload/frontend/branding/images/version-2/logo.png',
},
{
id: 3,
name: 'ZEE Marathi',
logo: 'https://akamaividz2.zee5.com/image/upload/frontend/branding/images/version-2/logo.png',
},
{
id: 4,
name: 'ZEE Bangla',
logo: 'https://akamaividz2.zee5.com/image/upload/frontend/branding/images/version-2/logo.png',
},
{
id: 5,
name: 'ZEE Tamil',
logo: 'https://akamaividz2.zee5.com/image/upload/frontend/branding/images/version-2/logo.png',
},
];
for (const data of channelData) {
try {
const descriptor = new ChannelDescriptorBuilder()
.identifier(`ch${data.id}`)
.majorNumber(data.id)
.minorNumber(0)
.build();
const metadata = new ChannelMetadataBuilder()
.name(data.name)
.channelType(ChannelTypeEnum.OTT)
.logoUrl(data.logo)
.build();
const channel = new ChannelInfoBuilder()
.channelDescriptor(descriptor)
.channelMetadata(metadata)
.build();
channels.push(channel);
console.debug(`Built OTT channel: ${data.name}`);
} catch (error) {
console.error(`Error building channel ${data.id}:`, error);
}
}
console.info(`Generated ${channels.length} channels`);
return channels;
};
// ------------------ Programs ------------------
export const fetchProgramLineup = async (): Promise<IProgram[]> => {
await delay(1500);
const programs: IProgram[] = [];
const now = Date.now();
for (let i = 1; i <= 5; i++) {
const descriptor = new ChannelDescriptorBuilder()
.identifier(`ch${i}`)
.majorNumber(i)
.build();
for (let j = 0; j < 5; j++) {
const start = now + j * 3600000;
const end = start + 3600000;
const program = new ProgramBuilder()
.identifier(`prog${i}-${j}`)
.channelDescriptor(descriptor)
.title(`Program ${j} on Channel ${i}`)
.startTimeMs(start)
.endTimeMs(end)
.build();
programs.push(program);
}
}
console.info(`Generated ${programs.length} programs`);
return programs;
};
// ------------------ Live Events ------------------
export const fetchLiveEventLineup = async (): Promise<ILiveEvent[]> => {
await delay(800);
const events: ILiveEvent[] = [];
const now = Date.now();
for (let i = 1; i <= 3; i++) {
const playback = new PlaybackReferenceBuilder()
.playbackType(PlaybackType.CONTENT)
.mediaId(new MediaId(`content${i}`, 'zee5_catalog'))
.build();
const event = new LiveEventBuilder()
.identifier(`live${i}`)
.eventType(EventType.SCHEDULED_EVENT)
.playbackReference(playback)
.startTimeMs(now)
.endTimeMs(now + 3600000)
.title(`Live Event ${i}`)
.build();
events.push(event);
}
console.info(`Generated ${events.length} live events`);
return events;
};
Playback Issues
If this is a playback issue, please provide your content URL, any pre-conditions (like geo-location), and let us know if it’s x86 or arm7.
<!-- Describe your playback issue if applicable -->
Please share the following details in addition:_
- Player SDK:
[Bitmovin, Shaka, ...] - Player SDK Version:
[e.g. 1.23]- Audio Codecs:
[AAC, ...] - Video Codecs:
[h.264, mp4] - Manifest Types:
[m3u8, dash, etc ..]
- Audio Codecs:
Q: If applicable, please provide your media/content url
If this is created dynamically, tokenized, etc please provide a way for us to access it
[N/A or Content / Media Url for testing]
Q: Are there any special headers required to reproduce the issue you are facing?
[N/A or Insert Headers]
Additionally please provide the following if possible
Provide Screenshots / Screengrabs / Logs. Please include as much information as you can that will help debug.
<!-- Answer here if applicable -->
Additional Context
Any Additional Context you would like to provide?
Add any other relevant information, such as recent updates to the SDK, dependencies, or device OS that may affect the bug.
I am doing some thing wrong. Please guide me how to do and what to do. I shared you all my code above what i have done.
I followed this below doc links. I missed some thing, but i dont understand. Please help me to understand and complete this feature
Step 1: Include the Package Dependencies | Design and Develop Vega Apps Step 2: Declare EPG Sync Task | Design and Develop Vega Apps Step 3: Define EPG Sync Task | Design and Develop Vega Apps Step 4: Implement EPG Sync Task | Design and Develop Vega Apps