Troubleshooting

This guide lists what goes wrong in the RTCode.io playground, by what you see, with the cause and the fix. Each entry stands on its own, so jump to the symptom. Where a problem has a guide of its own, the entry links to it.

First checks

Three looks answer most questions:

The output is blank, or stops updating

The output never starts

The output registers a service worker and announces that it is ready only after the registration succeeds. If the registration fails, or never finishes, nothing shows, and the editor gives up waiting for the output after 15 seconds. The cause is a browser that has no service workers, such as a private window of Firefox before version 139, or an embedded playground in a browser that restricts storage for frames of other sites. Open the playground in a normal window and reload with Ctrl+R / ⌘+R. For a frame, see the next entry. Browser support has the details.

The output shows Enable output

In a browser that has the Storage Access API, except Safari, an output that cannot register its service worker shows a button: Enable output, followed by while visiting and the site that embeds it. For a top-level playground that site is https://rtcode.io. Select the button. The browser asks for storage access, and the output reloads. Other playgrounds embedded in the same site reload with it.

Safari, and every browser on iPhone and iPad, shows neither the button nor a message. Open the playground in a tab of its own with Open β–Έ Playground β–Έ Tab, or follow its link.

The output stops following while you type

Unfinished markup is held back. The output keeps its last good state, and the playground reports i→o invalid with a reason. Sync resumes on the first keystroke that makes the markup valid.

ReasonWhat is unfinished
tagA tag such as <x or </x has no >.
nested-tagA new <x or </ starts while a tag is still open.
quoteAn attribute quote is never closed.
comment<!-- has no -->.
CDATA<![CDATA[ has no ]]>.
decl<!… or <?… has no >.
template<template> has no </template>, or its content is invalid.

An unclosed <script>, <style>, <textarea> or <title> is valid: the rest of the source becomes its text. A failure reads iβ†’o failed: … instead. For example, adding a shadow root to a host that is already live cannot be rolled back, so press F5. See Live sync and reloads.

A script fails

Open the console with F12. Uncaught errors and unhandled promise rejections of the page are logged as errors. Calls to console in the output are redirected to the playground, so they do not show in the browser's own developer tools for the frame, and the console is cleared on every full load. Console calls of a worker arrive with πŸ›ŽοΈβš™οΈ as their first argument, and the network panel lists every request that passes through the output's service worker.

Could not read or save the playground hash

A notice that covers the page reads Could not read or save the playground hash. followed by an error message. It has no close button, and the playground writes no more URL state until you reload. Three causes:

  • A compressed v=2 link that is not valid: Invalid compressed playground URL.
  • A link with an invalid value in an output setting key, such as odir=foo, ofs=-1 or ovp=390.5x844.
  • A browser without Uint8Array.prototype.toBase64 and fromBase64, opening a v=2 link, opening a large deployment's source, or saving a state of 8192 characters or more: Chrome and Edge before 140, Firefox before 133, Safari before 18.2 and Samsung Internet before 30.

Fix the link or update the browser, and reload. See Linking to and embedding the playground.

Changes do not show up

An exported file or the worker is stale

Exported files, worker:fs assets, worker code and the copies that sibling routes see reach the output only on a full reload. After a hot patch they keep the content of the last Load. Press F5. To make every edit a full reload, add reload to the exporting <script>, or sync=reload to a <meta>.

html
<script reload type=text/plain o:=notes.txt>Edit me and the iframe follows.</script>
<iframe src=notes.txt></iframe>

See Live sync and reloads and Exporting files.

Every edit reloads the output

A reload marker, reload or sync=reload, makes every edit a reload. So does a custom element that sync does not know: an element with a hyphen in its name that is not built in and not declared. Declare your elements with <meta io:sync>. A token that ends in a hyphen is a prefix.

html
<meta io:sync="tick-tock my-">

Sync treats pdf-file, pdf-page, zero-md and di-ff, and the prefixes ef-, edge-, show-, push-, img-, sl-, sp-, smart-, shin-, web-, chat- and c-, as safe. Web Awesome, Material, Ionic, UI5 and Vaadin elements, wa-, md-, ion-, ui5- and vaadin-, are not built in, so declare them. See <meta io:sync> and Writing IOSync-safe components.

A script edit restarts the page, or has no effect

A hot patch never runs a script. When you edit an executable script, add or remove one, or add pure to it, the output reloads, and its state is lost. A script marked pure is the exception in the other direction: an edit patches its text and the old code keeps running until you press F5.

A change in the page does not reach the source

Live output β†’ input works only while the input is not in focus, so click out of the editor, or press F6 to pull the output once. While a folder is shared with an agent, the output β†’ input direction is held, and F6 still works. Some changes never sync back: regions marked oi:exclude, writes that do not change a value, closed shadow roots, and live form properties such as the text typed into an <input>, a checkbox that you toggled or a <select> choice, because they change no attribute.

Content that a script created shows up in the source

Reverse sync writes changes of the light DOM into the source. Mark the container with oi:exclude, put generated markup in a closed shadow root, or turn Live output β†’ input off with Alt+O / βŒ₯+O. See Live sync and reloads.

The state of the page is gone after a reload

A full reload, F5 or any reload trigger, resets script state, the DOM and form values. It keeps the scroll position, the output settings, and the data of the output origin, such as localStorage and IndexedDB. The console and network panels are cleared.

Workers

Every request to the worker fails

When a handler returns a rejected promise with an Error, the response is an HTTP 500 with the stack. Any other failure ends in a network error, so a fetch() rejects with a TypeError, and a navigation shows the browser's error page. Throw Error objects, not strings, because other values may not be logged. Look for these causes:

CauseFix
A syntax errorThe error surfaces on every request, as a console error with the prefix πŸ›ŽοΈβš™οΈ, then ☁️ when the worker fails closed, the default, or β›… with io:fail=open. Fix the code. See Mocking third-party APIs.
export default { fetch }, or { fetch: handler }Not supported. Write fetch as an arrow function or a method of the object.
fetch: async function (r) {…}Not supported. Use an arrow function or a method.
Other properties in the export default objectKeep fetch as its only property. Properties before it can break the worker, and properties after it are dropped.
Another export statement, top-level await, import.meta or dynamic import()Not available in playground workers. Static import of URLs is bundled for you.
Two export default statements in different worker scriptsOnly the last is rewritten, and the first stays an export. Keep one.
A last line that is a // commentThe comment swallows the code that the playground appends. End the script with a line break.
A top-level variable called responseIt breaks export default workers. Rename it.
A failed importIf bundling fails, because of the network, an unresolvable import or the size budget, every request fails to compile until a later full load builds. Fix the import, then press F5. Run Clear: Worker Import Cache to build against current versions. A module host must allow CORS.
An empty worker, or one that holds only importsSee below.

A worker without a handler

A <script type=worker> that declares no handler, such as an empty one or one that holds only imports, can make requests fail with network errors in every playground that is open on the same output origin. It lasts until the browser restarts the service worker. Give the worker a fetch handler, then run Clear: Service Worker from the palette.

The worker never sees /

A request for / always returns your page, also when a worker exists. The worker answers every other path, and it answers / when you ask for a colon: <iframe src=:>, fetch(':api'). Write the colon first, because api:x is read as a URL scheme.

Top-level code runs more than once

The module of a worker is evaluated on every request, and twice for an export default worker, so a top-level console.log appears twice per request. The handler of the first evaluation is cached, so module-level state lasts across requests and reloads, until the worker source changes, the browser restarts the service worker, or you open another editor tab. In an export default worker, a top-level fetch(…) calls your own handler, not the network: start network calls inside the handler.

A stuck or stale service worker

Clear the service worker

Clear: Service Worker, in the palette, clears the worker import cache, then removes the service worker, the session state and the playground KV data of the current output origin. A toast says Cleared service worker. Press F5 afterwards.

Warning: The playground KV data of that origin is deleted with the service worker.

The key is Ctrl+Shift+W / ⌘+⇧+W, which Chromium reserves in a normal tab for closing the window, so use the palette: press F1 and type Clear.

Clear the worker import cache

Clear: Worker Import Cache, Ctrl+Shift+C / ⌘+⇧+C, forgets the bundles of the worker's imports. It does not reload: press F5 to build against the current version of every import. Reloading the playground clears the cache too.

Clear site data of an output origin

Playground β–Έ Reset opens the Clear Caches page at cc.rt.ht. Its RTCode.io and RTCode.net buttons open the /clear page of that domain. To reset one output origin, open its own page: https://out.rtcode.io/clear or https://out.rtcode.net/clear. Each answers Site data cleared! for that one origin. Popups, tabs, embedded playgrounds and Safari run on out.rtcode.net, so reset that one for them.

Popups and tabs are blocked

New Output Window opens the output on out.rtcode.net in a popup or a tab. Code: Versions and Compare open windows too. A browser can block them, although a keyboard shortcut counts as user activation. Allow pop-ups for rtcode.io, and run the command again.

Sharing with an agent

There is no Agent section

Agent πŸ“‚ Shareβ„’ needs a browser with FileSystemObserver, in practice Chrome or Edge 133 or later on a computer, and a playground that is not embedded. Elsewhere the Agent section of the File menu is hidden, and the palette has no Agent commands. In a frame, Open β–Έ Playground β–Έ Tab opens the playground at top level. See Browser support.

Another window is already sharing this route

The prompt reads Another window is already sharing this route. Unshare it and try again? Another window of the browser profile shares the same work folder, or a crashed session left its swap file. Choose Unshare & Retry. The playground asks the other windows to unshare, tries again after 200 ms, tries once to delete a leftover swap file, and tries a last time. If the folder is still taken, a toast says Another window is sharing. Cancel stops the share.

All playgrounds without a route share the folder Unrouted. To share a second one at the same time, Alt+click Share in the File menu: the folder gets a random suffix.

The Files differ dialog

At the start of a share, and while sharing, the playground compares the editor with πŸ“„.html and the exported files on disk. If both sides changed, the dialog lists each conflicting file with a Compare button:

Write

Keep the playground, and overwrite the folder.

Read

Take the folder: the disk πŸ“„.html, if there is one, and the exported files.

Cancel

The default. At the start it cancels the share, and during a share it unshares.

Compare opens diff.rt.ht in a popup with the disk text on one side and the playground on the other. Binary files show as hex. Two files that map to overlapping source, such as <section o:=card.html> that contains <style o:=card.css>, fail with Export edits overlap; edit one owning file at a time when both change together. Avoid editing the same content in both representations at the same time. Deleting an exported file on disk regenerates it from the editor: to remove an export, delete its declaration. See File Sync.

Sharing fails at the start

  • Root folder permission was not granted. means that you declined the browser's permission. The root stays linked: share again and allow it.
  • Stored root expired. Pick it again. means that the folder was moved or deleted. Pick it at the next Share.
  • Aborted means that you cancelled the folder picker.
  • Any other error shows a warning with its message, and the stored root is unlinked, unless the error was a permission error. The next Share asks for the folder again.
  • Share loads its guidance files and some modules each time. A failure of the network, or of the File Sync module, ends the share. Turn File Sync off with ✨ Agent: File Sync: Toggle in the palette to share without it.

See Agent πŸ“‚ Shareβ„’.

The address bar does not update while sharing

In Chromium browsers the playground stops writing the address bar while a folder is shared, and a toast says Folder sharing is active. The address bar is paused; reloads and deployments stay up to date. Do not copy the address bar then, because it holds the link from when sharing began. Use HashState.url() or Code.v, or unshare, which writes the current state back. A reload of the same tab brings the state back. If the browser cannot save it, a warning reads Could not save reload recovery. Keep this tab open or deploy your changes before reloading. See Linking to and embedding the playground.

Changes in the page are slow to reach the source

While a folder is shared, the output β†’ input direction waits, and the F6 badge shows when the page changed. Press F6 to pull the output into the source on demand. Input β†’ output sync is not affected.

Signing in and deploying

Sign-in messages

MessageWhat it means
Sign in to deploy., Sign in to delete., Sign in to view recent.You are signed out. The User menu opens. Sign in, then repeat the command.
User cancelled the flow!The GitHub window was closed before it finished. Try again, and allow pop-ups.
Required email address is missing.Your GitHub account has no email address to read. A button opens your GitHub profile settings.
Session has expired! Please sign in again!Your session ended. The User menu opens. Sign in again.
Your session has expired. Sign in again to deploy!The same, during a deploy. The playground signs you out.

Signing in works at top level. An embedded playground does not restore the session. See The playground interface.

Deploy messages

MessageWhat it means
No routes to deploy.The playground has no route. The Code dialog opens on Routes with a Deploy button.
Route is not yours.Another account owns the route, or you are an editor whose Editors list differs from the deployed one.
Route domain is not yours.The owner of the route has not claimed its domain.
storage quota exhaustedThe deployments of the account are over the size or the worker limit. The message does not say which. See Limits and quotas.
invalid old sizeAnother deploy changed the route meanwhile. Deploy again.
common owner requiredThe routes of one deploy belong to different owners.
Rate limit hit. Try again later!Deploy requests came too fast.

After most route-level errors the Routes dialog opens again with a Deploy button: change the routes and choose Deploy to retry once. See Deploying to RTEdge.net.

A key does nothing, or does something else

The deployed page differs from the playground

The colors differ after a deploy

The playground sets color-scheme: light dark on the output document, or the scheme that you forced, and on the output frame. A deployed page gets no such style. A page that relies on light-dark() or on the default colors without declaring its own color-scheme can look different once it is deployed. Declare it yourself:

html
<meta name=color-scheme content="light dark">

or :root { color-scheme: light dark } in a stylesheet.

Stored data is not there after a deploy

Playground KV lives in the browser, in the output origin, and is separate from the KV of RTEdge.net. A value in the playground may hold up to 64 KiB with its metadata, and up to 10 MiB on RTEdge.net. out.rtcode.io and out.rtcode.net have separate data, so popups, tabs and embedded playgrounds do not see what the inline output stored. See Playground and edge differences and Storing data in KV.

A script runs away Experimental

If a script in the output loops forever, the playground can stay responsive enough to leave it, because the output runs on another origin. Freeze moves the playground to frozen.rt.ht, where your code shows read-only. Run Freeze This Window, Ctrl+Alt+D / ⌘+βŒ₯+D, from the palette or the keyboard. Freeze All Windows, Ctrl+Alt+F / ⌘+βŒ₯+F, also freezes the other playground tabs of the browser profile.

The page offers one link, which becomes active after five seconds. If the code has scripts, it reads Review and unfreeze β€Ί: it reopens the playground without starting the output, and the playground shows Infinite loop? Fix code and reload! Fix the script and press Ctrl+R / ⌘+R. Without scripts, the link reads Unfreeze β€Ί and reopens the playground as it was.

Note: Freeze is a work in progress and may change. See Output windows.

A ? link shows 404 or does not open the editor

Write the slash before the question mark: https://example.com/docs/?. A bare ? must follow a slash. The edge adds the slash for paths without a dot, and the playground host rtcode.io itself answers 404 for rtcode.io/?. A custom domain path, and a route with a dotted host through in.rt.ht, needs the slash written out. See Linking to and embedding the playground.

A link stores the layout, but not the position of the split. The playground keeps one position for each axis during the session. Use Alt+1 / βŒ₯+1 to Alt+9 / βŒ₯+9 to set it again.

A link opens an empty editor

A link without a source under / opens an empty editor, with a hint, in a top-level playground. An embedded playground loads a starter page instead. A link with an act key and a source ignores the action.

See also