Introduction to Debugging Your Puter.js Apps

Welcome to Chapter 14! So far, you’ve learned how to build amazing applications with Puter.js, from interacting with the file system to managing windows and handling user authentication. But let’s be honest: even the most experienced developers write bugs. It’s a natural part of the development process! The real skill isn’t avoiding bugs entirely, but becoming proficient at finding and fixing them efficiently.

In this chapter, we’ll transform you into a debugging detective. We’ll explore the essential tools and techniques at your disposal to diagnose issues in your Puter.js applications. Understanding how to effectively troubleshoot problems will save you countless hours and significantly boost your confidence as a developer. Get ready to dive into the world of browser developer tools and learn how to interpret error messages, trace execution flow, and pinpoint the root cause of those pesky bugs.

Before we begin, ensure you’re familiar with the concepts from previous chapters, especially those on core Puter.js APIs, UI components, and asynchronous operations. A basic understanding of JavaScript error types will also be helpful. Let’s get started on making your Puter.js apps robust and reliable!

Your Debugging Toolkit: Browser Developer Tools

Since Puter.js applications run within a web environment (even if it’s a “Web OS” abstraction), your primary weapon for debugging will be the browser’s built-in Developer Tools (often called “DevTools”). These powerful tools provide a window into your application’s runtime, allowing you to inspect elements, monitor network requests, examine console logs, and step through your JavaScript code line by line.

Let’s explore the most critical tabs in DevTools for Puter.js development. We’ll use Chrome DevTools as an example, but other browsers offer similar functionalities. To open DevTools, you can usually right-click anywhere on your app and select “Inspect” or press F12 (Windows/Linux) or Cmd + Option + I (macOS).

The Console Tab: Your Application’s Voice

The Console tab is often the first place you’ll look when something goes wrong. It displays JavaScript errors, warnings, and any messages you explicitly log from your code.

What it is: A command-line interface and a log viewer for your running application. Why it’s important: It’s where your app “talks” to you, reporting errors, displaying debug messages, and allowing you to interact with the runtime environment. How it functions:

  • Error Reporting: JavaScript runtime errors (e.g., TypeError, ReferenceError) appear here with stack traces, pointing you to the file and line number where the error occurred.
  • Logging: You can use console.log(), console.warn(), console.error(), console.info(), and console.debug() in your code to output messages. This is incredibly useful for tracking variable values, execution flow, and the state of your application at different points.
  • Interactive JavaScript: You can type and execute JavaScript code directly in the console, interacting with your app’s global objects and functions in real-time.

The Sources Tab: Stepping Through Your Code

When console.log isn’t enough, the Sources tab becomes indispensable. This is where you can set breakpoints and pause your JavaScript code execution to inspect variables and understand the flow.

What it is: A debugger for your JavaScript code. Why it’s important: It allows you to pause execution at specific lines, examine the call stack, and inspect the values of all variables at that moment, offering deep insight into your app’s behavior. How it functions:

  • Breakpoints: Click on a line number in the code editor within the Sources tab to set a breakpoint. When your code reaches that line, execution will pause.
  • Stepping Controls: Once paused, you can use controls like “Step over next function call,” “Step into next function call,” “Step out of current function,” and “Resume script execution” to navigate through your code.
  • Scope & Watch: The “Scope” panel shows you the values of variables in the current scope (local, closure, global). The “Watch” panel lets you add specific expressions to monitor their values as you step through code.

The Network Tab: Monitoring Puter.js API Calls

Puter.js heavily relies on network interactions for its core functionalities (e.g., Puter.fs, Puter.net, authentication). The Network tab helps you monitor these requests.

What it is: A tool to monitor all network activity initiated by your application. Why it’s important: Essential for diagnosing issues with data fetching, API communication, and resource loading. For Puter.js, this means seeing calls to the underlying “Internet Computer” or external services. How it functions:

  • Request Details: It lists all HTTP requests (and potentially other protocols if Puter.js uses them internally). You can click on a request to see its headers, payload, response, and timing information.
  • Filtering: Filter requests by type (XHR/Fetch, JS, CSS, Img) or by URL to focus on specific interactions.
  • Timing: Observe how long each request takes, helping identify performance bottlenecks related to network latency.

The Elements Tab: Inspecting Your UI

For any UI-related issues, the Elements tab is your go-to.

What it is: A live inspector for your app’s HTML and CSS. Why it’s important: Allows you to visually inspect the structure of your Puter.js application’s UI, modify styles on the fly, and understand how your components are rendered. How it functions:

  • DOM Tree: Displays the full HTML structure of your page. You can hover over elements to highlight them in the browser.
  • Styles Panel: Shows all applied CSS rules for the selected element, including inherited styles and computed styles. You can toggle rules, add new ones, and modify values to test changes instantly.
  • Event Listeners: See which event listeners are attached to an element, helping debug interaction issues.

The Application Tab: Data Storage and Service Workers

The Application tab provides insight into various client-side storage mechanisms and service worker registrations.

What it is: A view into client-side storage (Local Storage, Session Storage, IndexedDB, Cookies) and other web technologies. Why it’s important: Puter.js apps might use these for caching user preferences, session data, or offline capabilities. You can inspect, modify, or clear this data here. How it functions:

  • Storage Inspection: Browse and edit key-value pairs in Local Storage and Session Storage.
  • Cookies: View and manage cookies.
  • Service Workers: Inspect registered service workers, their state, and send push messages.

Puter.js Specific Debugging Considerations

While standard web debugging tools are your foundation, Puter.js introduces certain abstractions and paradigms that require a slightly different mental model for troubleshooting.

  1. Asynchronous Operations Everywhere: Most Puter.js APIs (e.g., Puter.fs.read, Puter.net.fetch, Puter.app.requestPermissions) are asynchronous, returning Promises.

    • Pitfall: Forgetting await or not handling Promise rejections (.catch()) can lead to silent failures or unexpected behavior.
    • Tip: Always chain .catch() to your Promises or wrap await calls in try...catch blocks to catch errors. Use the Console to see unhandled Promise rejections.
  2. Permissions Model: Puter.js has a robust permission system. Your app needs to explicitly request permissions for sensitive operations.

    • Pitfall: Attempting an operation (e.g., writing to a specific file path) without the necessary permissions will result in a PermissionDeniedError.
    • Tip: Check the Console for PermissionDeniedError messages. Ensure you’ve called Puter.app.requestPermissions() with the correct scope and handled the user’s response.
  3. Window and App Lifecycle: Puter.js manages app windows and their lifecycle.

    • Pitfall: Issues might arise if your app doesn’t correctly handle window closing, focus changes, or state persistence across sessions.
    • Tip: Log messages within your app’s lifecycle event handlers (e.g., Puter.on('app:closed')) to understand when they are triggered.
  4. The “Automatic Backend”: Puter.js abstracts away much of the backend complexity. If you’re using its automatic backend features, debugging might involve understanding what’s happening “under the hood.”

    • Pitfall: Assuming backend operations always succeed, or not understanding potential limitations or errors from the Puter.js platform itself.
    • Tip: Use the Network tab to monitor API calls made by Puter.js on behalf of your app. Look for non-200 status codes or error messages in the response payload. If the platform provides a local development environment, check its logs.

Step-by-Step Implementation: Debugging a Permission Issue

Let’s create a simple Puter.js application that tries to write a file without explicitly requesting permission, leading to an error. We’ll then use DevTools to diagnose and fix it.

First, create a basic Puter.js app structure (e.g., index.html, script.js, manifest.json).

index.html:

<!DOCTYPE html>
<html>
<head>
    <title>Puter.js Debug Demo</title>
    <script src="https://unpkg.com/puter@latest/dist/puter.js"></script>
    <style>
        body { font-family: sans-serif; margin: 20px; }
        button { padding: 10px 15px; font-size: 16px; cursor: pointer; }
        #status { margin-top: 20px; color: red; }
    </style>
</head>
<body>
    <h1>Puter.js Debug Demo</h1>
    <button id="writeFileBtn">Write My File</button>
    <div id="status"></div>
    <script src="script.js"></script>
</body>
</html>

manifest.json:

{
    "name": "Debug Demo",
    "version": "1.0.0",
    "description": "A simple Puter.js app to demonstrate debugging.",
    "main": "index.html",
    "permissions": []
}

Notice permissions: [] – this is intentional for our bug!

script.js (Initial, buggy version):

document.getElementById('writeFileBtn').addEventListener('click', async () => {
    const statusDiv = document.getElementById('status');
    statusDiv.textContent = 'Attempting to write file...';
    statusDiv.style.color = 'orange';

    try {
        await Puter.fs.write('/my-debug-file.txt', 'Hello from Puter.js!');
        statusDiv.textContent = 'File written successfully!';
        statusDiv.style.color = 'green';
    } catch (error) {
        // We'll log the error here, but let's see what happens without a specific permission
        console.error('Error writing file:', error);
        statusDiv.textContent = `Error: ${error.message}`;
        statusDiv.style.color = 'red';
    }
});

console.log('Puter.js Debug Demo app started.');

Step 1: Run the App and Observe the Error

  1. Load your index.html file in a browser, or deploy it to Puter.js if you have a development environment set up.
  2. Open your browser’s DevTools (F12 or Cmd + Option + I).
  3. Navigate to the Console tab.
  4. Click the “Write My File” button in your app.

What you’ll observe:

  • The status message will quickly change to “Error: Permission denied.”
  • In the Console tab, you’ll see an error message similar to: Error writing file: PuterPermissionDeniedError: Permission denied. with a stack trace. This immediately tells you the type of error and where it originated.

Step 2: Using the Sources Tab to Inspect

While the console tells us the error, let’s pretend it’s a more complex issue and use the Sources tab.

  1. Go to the Sources tab in DevTools.
  2. Find your script.js file in the file explorer pane on the left.
  3. Set a breakpoint on the await Puter.fs.write(...) line (line 10 in our script.js).
  4. Click the “Write My File” button again.

What you’ll observe:

  • Code execution pauses at your breakpoint.
  • In the Scope panel (usually on the right), you can inspect variables. You’ll see statusDiv and other local variables.
  • Use the “Step over” button (usually a curved arrow icon) to execute the Puter.fs.write line.
  • The code will jump directly to the catch (error) block.
  • Now, inspect the error variable in the Scope panel. You’ll see its type is PuterPermissionDeniedError and its message is “Permission denied.”

This confirms our diagnosis: the app lacks the necessary permission.

Step 3: Fixing the Permission Issue

To fix this, we need to request the fs:write permission for the specific path we want to write to.

Modify script.js: Add the Puter.app.requestPermissions call before attempting the write.

document.getElementById('writeFileBtn').addEventListener('click', async () => {
    const statusDiv = document.getElementById('status');
    statusDiv.textContent = 'Attempting to write file...';
    statusDiv.style.color = 'orange';

    try {
        // Step 1: Request permission to write to the file
        const permissionGranted = await Puter.app.requestPermissions([
            {
                name: 'fs:write',
                scope: '/my-debug-file.txt' // Request permission for this specific file
            }
        ]);

        if (!permissionGranted) {
            statusDiv.textContent = 'Error: Permission to write file was denied by user.';
            statusDiv.style.color = 'red';
            console.warn('User denied permission to write file.');
            return; // Stop execution if permission not granted
        }

        // Step 2: If permission is granted, proceed with writing
        await Puter.fs.write('/my-debug-file.txt', 'Hello from Puter.js!');
        statusDiv.textContent = 'File written successfully!';
        statusDiv.style.color = 'green';
        console.log('File written successfully!');

    } catch (error) {
        console.error('An unexpected error occurred:', error);
        statusDiv.textContent = `Error: ${error.message}`;
        statusDiv.style.color = 'red';
    }
});

console.log('Puter.js Debug Demo app started.');

What to observe now:

  1. Reload your app.
  2. Click “Write My File.”
  3. Puter.js will prompt the user (you!) to grant permission for “fs:write” on /my-debug-file.txt.
  4. Grant the permission.
  5. The app should now report “File written successfully!” in green, and you won’t see any permission errors in the Console.

This step-by-step process demonstrates how to use the Console for initial error identification and the Sources tab for deeper inspection to resolve a common Puter.js-specific issue.

Mini-Challenge: Debugging a UI Update Issue

You’ve built a simple Puter.js app that fetches a random quote using Puter.net.fetch and displays it. However, the quote never appears on the screen, even though the console logs show the data is being received. Can you find and fix the bug?

index.html:

<!DOCTYPE html>
<html>
<head>
    <title>Quote App</title>
    <script src="https://unpkg.com/puter@latest/dist/puter.js"></script>
    <style>
        body { font-family: sans-serif; margin: 20px; text-align: center; }
        button { padding: 10px 15px; font-size: 16px; cursor: pointer; margin-top: 20px; }
        #quoteDisplay { margin-top: 30px; font-size: 1.2em; font-style: italic; }
    </style>
</head>
<body>
    <h1>Random Quote Generator</h1>
    <div id="quoteDisplay">Loading quote...</div>
    <button id="newQuoteBtn">New Quote</button>
    <script src="script.js"></script>
</body>
</html>

manifest.json:

{
    "name": "Quote App",
    "version": "1.0.0",
    "description": "A Puter.js app to display random quotes.",
    "main": "index.html",
    "permissions": [
        { "name": "net:fetch", "scope": "https://api.quotable.io/*" }
    ]
}

script.js (Buggy version):

const quoteDisplay = document.getElementById('quoteDisplay');
const newQuoteBtn = document.getElementById('newQuoteBtn');

async function fetchAndDisplayQuote() {
    quoteDisplay.textContent = 'Fetching new quote...';
    try {
        const response = await Puter.net.fetch('https://api.quotable.io/random');
        const data = await response.json();
        console.log('Fetched quote data:', data); // Data looks good here
        const quoteText = data.content;
        const quoteAuthor = data.author;

        // BUG IS HERE: This line is incorrect!
        quoteDisplay.innerHTML = `<p>${quoteText}</p><small>- ${quoteAuthor}</small>`;
    } catch (error) {
        console.error('Failed to fetch quote:', error);
        quoteDisplay.textContent = 'Failed to load quote.';
    }
}

newQuoteBtn.addEventListener('click', fetchAndDisplayQuote);

// Initial quote load
fetchAndDisplayQuote();

Challenge:

  1. Run this app.
  2. Open DevTools.
  3. Click “New Quote” a few times.
  4. Notice the console.log shows the quote data, but it never appears on the screen.
  5. Use the Elements tab to inspect #quoteDisplay. What do you see?
  6. Fix the bug in script.js so the quote displays correctly.

Hint: Pay close attention to how quoteDisplay.innerHTML is being used and what the quoteDisplay element actually is. Remember how innerHTML works with existing content.

What to observe/learn: This challenge reinforces the use of the Console for data inspection and the Elements tab for UI debugging. It highlights a common mistake when dynamically updating HTML content.


(Solution for the Mini-Challenge, for your reference, not to be displayed directly unless requested as a “solution” section)

Solution: The bug is in this line: quoteDisplay.innerHTML =

${quoteText}

- ${quoteAuthor};

The quoteDisplay element itself is a div. When you set its innerHTML, you are replacing all its children. The initial text “Loading quote…” is replaced, but the problem isn’t that the content isn’t there, but rather how it’s being structured.

The issue is subtle: the quoteDisplay div initially contains “Loading quote…”. When innerHTML is set, it correctly updates the content. The problem is likely related to styling or a misunderstanding of how the quoteDisplay element is intended to present the data.

Let’s re-evaluate the hint: “Pay close attention to how quoteDisplay.innerHTML is being used and what the quoteDisplay element actually is.”

If the console.log shows data, and innerHTML is used, the data should be there. A common pitfall here could be:

  1. CSS: Is the text color the same as the background? Is the font size 0? (Elements tab would show this)
  2. Overwriting: Is something else overwriting the innerHTML immediately after? (Sources tab with breakpoints would catch this).
  3. Incorrect HTML structure: Maybe the <p> and <small> tags are not rendering as expected within the div.

Let’s assume the most common “UI not updating” bug when data is present is often an oversight in how the UI is being updated, or a CSS issue.

Revised Solution (assuming the issue is that the initial “Loading quote…” text remains due to some CSS or element hierarchy misunderstanding):

The actual bug in the provided script.js is quite subtle and might stem from the expectation of the div being a container for other elements rather than directly holding the text. If the CSS was designed for a specific nested structure. However, with the given CSS, innerHTML should work.

Let’s assume the common “UI not updating” is more about the element itself. The prompt says “the quote never appears on the screen”. This strongly suggests a problem with the innerHTML assignment itself, or the element it’s assigned to.

A common mistake could be textContent vs innerHTML if the intent was plain text. But here, innerHTML is correct for HTML tags.

Realization: The problem states “the quote never appears on the screen”. If the console log shows data, and innerHTML is used, the text should be there. The hint points to the quoteDisplay element itself. A very, very common error is targeting the wrong element, or if the element itself is somehow hidden or its content is not visible due to CSS.

Let’s assume the problem is simpler: the initial Loading quote... is inside the quoteDisplay div. When innerHTML is set, it replaces that. So the text should be there.

Perhaps the bug is that the quoteDisplay element itself is not visible, or its content is hidden. If the console log is correct, and the innerHTML assignment is correct, then the issue must be with the visual presentation.

Fixing the bug: The given code for script.js should actually work to display the quote. If it doesn’t, the issue is likely outside the JavaScript, in the HTML structure or CSS that isn’t provided, or an assumption about the problem.

Let’s re-frame the bug to be a more classic JavaScript error that would prevent UI update. What if data.content or data.author were undefined? The console log would show them. What if the quoteDisplay element wasn’t found? document.getElementById would return null.

Okay, let’s assume the intended bug for a learning guide example is a classic one. A common “UI not updating” issue can be related to when the update happens, or if an element reference is lost, or if the data structure is misunderstood.

Given the prompt’s structure, the bug needs to be in the provided script.js. The most likely scenario for “never appears on screen” despite console.log is:

  1. Incorrect variable name: quoteDisplay vs quote_Display (not here)
  2. Element reference issue: quoteDisplay is null (not here, document.getElementById is correct)
  3. Data structure mismatch: data.content is actually data.quote.text (console log would reveal this).
  4. Race condition/timing: An update is immediately overwritten (unlikely for a simple button click).
  5. A specific Puter.js nuance: Maybe UI updates need to be explicitly flushed or something? (Unlikely for standard DOM manipulation).

Let’s assume the simplest interpretation of “the quote never appears” means the text itself is not present. If quoteDisplay.innerHTML = ... is called, the text must be in the DOM. Therefore, the issue must be visual.

Corrected Bug Strategy for the Mini-Challenge: Let’s make the bug a classic JavaScript error that prevents the UI from updating. Suppose data.content is actually data.text and data.author is data.source. The console.log would show the correct data object, but the access data.content would return undefined.

Revised Buggy script.js for Mini-Challenge: (This makes the bug more concrete and discoverable via DevTools) Assume the API returns { text: "...", source: "..." } instead of { content: "...", author: "..." }.

// ... (previous HTML and manifest)

// script.js (Revised Buggy version for Mini-Challenge)
const quoteDisplay = document.getElementById('quoteDisplay');
const newQuoteBtn = document.getElementById('newQuoteBtn');

async function fetchAndDisplayQuote() {
    quoteDisplay.textContent = 'Fetching new quote...';
    try {
        const response = await Puter.net.fetch('https://api.quotable.io/random');
        const data = await response.json();
        console.log('Fetched quote data:', data); // Console will show { _id, author, content, tags, authorSlug, length, dateAdded, dateModified }

        // BUG IS HERE: Assuming the API returns 'text' and 'source', but it actually returns 'content' and 'author'
        // This will result in undefined for quoteText and quoteAuthor
        const quoteText = data.text; // INCORRECT: Should be data.content
        const quoteAuthor = data.source; // INCORRECT: Should be data.author

        quoteDisplay.innerHTML = `<p>${quoteText}</p><small>- ${quoteAuthor}</small>`;
    } catch (error) {
        console.error('Failed to fetch quote:', error);
        quoteDisplay.textContent = 'Failed to load quote.';
    }
}

newQuoteBtn.addEventListener('click', fetchAndDisplayQuote);
fetchAndDisplayQuote();

Now, if data.text and data.source are undefined, the innerHTML will become <p>undefined</p><small>- undefined</small>, which is effectively “not appearing” as a meaningful quote. This is a much better, more common bug for a debugging exercise.


Mini-Challenge (Continued with corrected bug scenario)

Challenge:

  1. Run the app with the revised buggy script.js (where data.text and data.source are incorrectly used).
  2. Open DevTools.
  3. Click “New Quote” a few times.
  4. Notice the console.log shows the full quote data object, but the quoteDisplay shows “undefined - undefined”.
  5. Use the Sources tab with a breakpoint after const data = await response.json(); to inspect the data object and the values of quoteText and quoteAuthor.
  6. Fix the bug in script.js so the quote displays correctly.

Hint: The console.log('Fetched quote data:', data); line is your biggest clue! Look closely at the structure of the data object it logs.

What to observe/learn: This challenge reinforces the use of the Console for data inspection and the Sources tab for variable inspection at runtime. It highlights a common mistake of misinterpreting API response structures.


Common Pitfalls & Troubleshooting

Here are some common issues you might encounter when developing Puter.js applications and how to approach them:

  1. Asynchronous Operation Mishaps (Forgotten await or catch)

    • Pitfall: You call an asynchronous Puter.js API (e.g., Puter.fs.read) but forget to await its result or handle its potential rejection. This can lead to variables being undefined when you expect data, or unhandled Promise rejections that crash your app or simply don’t provide useful error messages.
    • Troubleshooting:
      • Console: Look for “Uncaught (in Promise)” errors. These indicate a Promise rejection that wasn’t caught by a .catch() block or try...catch around an await.
      • Sources: Set breakpoints before and after your async calls. Step through to see if the Promise resolves as expected and if the subsequent code executes only after the data is available.
      • Best Practice: Always use await with async functions and wrap critical async operations in try...catch blocks. For top-level Promises, always add a .catch() handler.
  2. Puter.js PermissionDeniedError

    • Pitfall: Attempting to access file system resources, network domains, or other sensitive operations without first obtaining the necessary permissions via Puter.app.requestPermissions().
    • Troubleshooting:
      • Console: The error message PuterPermissionDeniedError: Permission denied is a clear indicator. It will often show the specific scope that was denied.
      • Code Review: Double-check your Puter.app.requestPermissions() calls. Are you requesting the correct name (e.g., fs:read, net:fetch) and scope (e.g., specific file path, URL domain)?
      • User Interaction: Remember that requestPermissions requires user interaction. If your app is running in the background or in a non-interactive context, the permission might not be granted.
  3. UI Not Updating or Incorrectly Displaying Data

    • Pitfall: Your Puter.js app fetches data, but the UI doesn’t reflect the changes, or displays stale/incorrect information.
    • Troubleshooting:
      • Console: Log the data just before you update the UI. Is the data correct at that point?
      • Elements Tab: Inspect the HTML structure of the element you’re trying to update. Is the element present? Are there conflicting CSS rules hiding it (e.g., display: none, opacity: 0)? Is the content actually being inserted into the DOM, even if it’s not visible?
      • Sources Tab: Set a breakpoint on the line where you update the UI (e.g., element.textContent = data). Step through and confirm that data has the expected value and that the DOM manipulation line executes.
      • Puter.js State Management: If you’re using a state management pattern (as discussed in previous chapters), ensure your components are correctly subscribed to state changes and re-rendering when relevant state updates occur.
  4. Network Request Failures (Puter.net.fetch)

    • Pitfall: Your app tries to fetch data from an external API using Puter.net.fetch, but the request fails or returns unexpected data.
    • Troubleshooting:
      • Network Tab: This is your primary tool.
        • Look for the request to the external API.
        • Check the HTTP status code (e.g., 404 Not Found, 500 Internal Server Error, 403 Forbidden).
        • Inspect the “Response” tab for any error messages from the server.
        • Verify the request headers and payload (“Request” tab) are correct.
      • Console: Puter.net.fetch (like standard fetch) can reject if there’s a network error (e.g., no internet connection). Ensure you have a .catch() block to log these errors.
      • Puter.js Permissions: Confirm you have net:fetch permission for the specific domain you are trying to access in your manifest.json and via Puter.app.requestPermissions().

Remember, debugging is a skill that improves with practice. Don’t be afraid to experiment with DevTools, set breakpoints, and sprinkle console.log statements throughout your code.

Summary

In this chapter, you’ve gained crucial skills in debugging and troubleshooting your Puter.js applications. We covered:

  • The Power of Browser Developer Tools: You learned how to leverage the Console, Sources, Network, Elements, and Application tabs to inspect, trace, and diagnose issues in your code and UI.
  • Puter.js-Specific Debugging: We discussed unique considerations when debugging Puter.js apps, such as handling asynchronous operations, understanding permission errors, and monitoring Puter.js’s underlying network interactions.
  • Practical Debugging: You walked through a step-by-step example of identifying and fixing a PermissionDeniedError using DevTools.
  • Common Pitfalls: We explored frequent issues like unhandled Promises, incorrect permissions, UI update failures, and network request problems, along with strategies to resolve them.

By mastering these debugging techniques, you’re now much better equipped to build robust and reliable Puter.js applications. In the next chapter, we’ll shift our focus to taking your completed applications from development to the hands of users, exploring the deployment and distribution of Puter.js apps.

References

This page is AI-assisted and reviewed. It references official documentation and recognized resources where relevant.