How to Debug Failed IPTV Stream Tests: A Complete Guide

To debug failed IPTV stream tests, run npm run playlist:test, inspect the live status table for error codes like TIMEOUT or HTTP_404_NOT_FOUND, and use NODE_ENV=test with mock data for deterministic reproduction.

The iptv-org/iptv repository uses an automated testing suite to validate thousands of stream URLs across its playlist files. When the playlist:test command flags streams as failed, understanding the underlying error classification system and debugging tools will help you identify whether a link is temporarily down, permanently moved, or malformed.

Understanding the IPTV Stream Test Architecture

The testing pipeline relies on three core components working sequentially: network requests via axios, media analysis via mediainfo.js, and error normalization in the StreamTester class.

Core Testing Components

In scripts/core/streamTester.ts, the test() method orchestrates the validation:

  1. Network Request: Fetches the stream URL with configurable timeouts and proxy support.
  2. Media Analysis: Passes the response buffer to mediainfo.js to detect video tracks.
  3. Result Classification: Returns a StreamTesterResult object containing an ok boolean and a specific error code.

Error Classification System

The scripts/commands/playlist/test.ts file defines errorStatusCodes, an array that determines which failures constitute critical errors versus warnings:

const errorStatusCodes = [
  'ECONNREFUSED',
  'ENOTFOUND',
  'ENETUNREACH',
  'EPROTO',
  'HTTP_404_',
  'HTTP_404_NOT_FOUND',
  'HTTP_404_UNKNOWN_ERROR',
  'HTTP_410_GONE'
];

Streams matching these codes without a label property increment the error counter; labeled streams generate warnings instead.

Running the Test and Interpreting Results

Execute the validation against specific playlist files using the npm script defined in package.json:

cross-env DATA_DIR=tests/__data__/input/data ROOT_DIR=tests/__data__/output \
npm run playlist:test streams/ag.m3u

The command renders a live table that updates every few seconds with the following columns:

  • tvg-id: The stream identifier from the playlist.
  • url: The truncated stream URL.
  • label: Optional human-readable metadata.
  • status: The result code (OK, TIMEOUT, HTTP_404_NOT_FOUND, NO_VIDEO, etc.).

Upon completion, the CLI outputs a summary such as 2 problems (1 errors, 1 warnings). Red entries in the table indicate streams that triggered the failure.

Identifying Common Failure Reasons

Network and HTTP Errors

In scripts/core/streamTester.ts (lines 96-107), the error handler normalizes axios exceptions into predictable codes:

  • TIMEOUT: Triggered when a request exceeds the configured timeout or is explicitly canceled.
  • HTTP_<status>_<text>: Derived from error.response.status and statusText (e.g., HTTP_404_NOT_FOUND).
  • AXIOS_<code>: Captures low-level network errors like ECONNREFUSED or ENOTFOUND when no response is received.

Media Analysis Failures

Even when a URL responds successfully, the stream must contain valid video tracks. The mediainfo.js analysis (lines 78-91) returns:

  • OK: At least one video track detected.
  • NO_VIDEO: The response parsed successfully but contained no video streams (common for radio streams or corrupted playlists).

Debugging Techniques for Failed Streams

Using Verbose Logging and Isolation

To inspect the raw StreamTesterResult for a specific stream, temporarily add logging to the runTest function in scripts/commands/playlist/test.ts:

async function runTest(stream: Stream) {
  const result = await tester.test(stream);
  console.log(`URL: ${stream.url}, Result:`, result);
  // ...existing logic...
}

For easier reading, limit parallelism to one stream at a time:

npm run playlist:test streams/ag.m3u -- --parallel 1

Testing Mode with Mock Data

Set NODE_ENV=test to bypass network requests and use deterministic mock data from tests/__data__/input/playlist_test/results.js:

cross-env NODE_ENV=test DATA_DIR=tests/__data__/input/data \
npm run playlist:test streams/ag.m3u

In this mode, scripts/core/streamTester.ts (line 58) imports the mock map:

if (TESTING) {
  const results = (await import('../../tests/__data__/input/playlist_test/results.js')).default;
  return results[stream.url as keyof typeof results];
}

Edit results.js to simulate specific error conditions without external dependencies:

export default {
  "https://example.com/broken.m3u8": { status: { ok: false, code: "HTTP_404_NOT_FOUND" } },
  "https://example.com/good.m3u8": { status: { ok: true, code: "OK" } }
};

Automatic Cleanup with --fix Flag

Once you have confirmed which streams are permanently broken, use the --fix flag to automatically remove them:

npm run playlist:test streams/ag.m3u -- --fix

This invokes removeBrokenLinks() (lines 55-64 in scripts/commands/playlist/test.ts), which:

  1. Groups streams by their source playlist file.
  2. Filters out streams where isBroken(stream) returns true.
  3. Overwrites the original playlist files with the cleaned entries.

The isBroken() function (lines 12-18) applies the same logic as the error counter, ensuring only unlabeled streams with critical error codes are purged.

Summary

  • Run tests with npm run playlist:test <playlist> to generate a live status table showing OK, TIMEOUT, HTTP_404_NOT_FOUND, and other codes.
  • Interpret failures by comparing status codes against the errorStatusCodes array in scripts/commands/playlist/test.ts to distinguish critical errors from warnings.
  • Debug efficiently by adding temporary logging to runTest(), using --parallel 1 for serial output, or setting NODE_ENV=test to use mock data from tests/__data__/input/playlist_test/results.js.
  • Clean playlists automatically with the --fix flag, which triggers removeBrokenLinks() to purge streams that return codes like HTTP_410_GONE or ECONNREFUSED.

Frequently Asked Questions

Why does a stream show HTTP_404_NOT_FOUND when it works in my browser?

The HTTP_404_NOT_FOUND code indicates the server returned a 404 status to the axios request in scripts/core/streamTester.ts. This often happens when the server blocks automated requests by checking for specific headers or user-agents that the test client does not send. Try comparing the headers your browser sends with the default axios configuration, or check if the stream requires a referer or specific cookie that the test environment lacks.

How do I debug a specific stream without running the entire playlist?

Set NODE_ENV=test and edit the tests/__data__/input/playlist_test/results.js file to include only the URL you want to debug. Then run the test command with --parallel 1 to process streams serially. This isolates the output for your target stream without network variability, allowing you to verify how the CLI handles specific error codes like NO_VIDEO or TIMEOUT.

What is the difference between an error and a warning in the test output?

Errors are counted when a stream returns a code listed in errorStatusCodes (such as HTTP_404_NOT_FOUND or ECONNREFUSED) and has no label property. Warnings are generated for the same error codes if the stream has a label, or for non-critical issues. The --fix flag only removes streams classified as errors, preserving labeled streams even if they fail, according to the logic in isBroken() within scripts/commands/playlist/test.ts.

Can I use a proxy to debug geo-blocked streams?

Yes, pass the -x or --proxy flag followed by your proxy URL when running the test command. The StreamTester class passes this option to the axios client in scripts/core/streamTester.ts, routing all requests through the specified proxy. If all streams suddenly return TIMEOUT after adding the flag, verify the proxy is running and accessible from your environment.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →