Troubleshooting
Start from the symptom. Before changing code, identify the execution context, because the same symptom has different causes in different contexts. Evidence labels have the same meaning as on that page.
Blank weblet
Likely causes
- The frame request failed with 401 or 403, or the app is inactive.
- The weblet is on a
.formview, where plain output is discarded. - The asset has no deployed code, so the platform returns an empty response.
- Output text was discarded by result propagation.
- A script error occurred after the frame loaded.
How to confirm
- Open browser developer tools and find the request for the frame.
- Read its status and response body. Status 200 with an empty body means the script produced nothing.
- Check the
viewending and thetypein the manifest. - Run
zeysdk compareto confirm that the deployed code matches yours.
Fix
Correct the status cause, deploy the code, or move the weblet to a view that matches how it renders. Use form commands in .form views and output elsewhere. zeysdk run cannot reproduce this problem, because it has no session and no frame.
A protected route accepts a request with no token
Likely causes
- The handler reads
AUTHwithout<use var="AUTH" />, so a query argument or form field supplies it. - The check sits before
rest:server, and the handler still reads a forgeable$AUTH.*. - The handler has no check. The platform does not reject anonymous calls for you, even for a restricted application.
How to confirm
Send ?AUTH[user]=1 with no token, using curl -g. A 200 confirms the problem.
Fix
Add use as the first child of every rest:resource, for every global the handler reads. See Bind globals inside rest:resource. Treat any past use of such a route as unauthenticated.
Unauthenticated or forbidden service call from a weblet
Likely causes
- The weblet's JavaScript called
REMOTECALL, and the cookie does not authenticate that call, soAUTHis unset. - The
Authorizationheader holds an SDK token, not an access token. - The application is restricted and the token's user lacks permission, which gives 403. An anonymous call is not stopped by this check and still runs.
- The service or application is inactive, which also gives 403.
How to confirm
Inspect the failing request in developer tools and check for an Authorization header. Repeat the call with curl and a known-good access token. If that works, the service is fine and the browser call is the problem.
Fix
Prefer calling the resource server-side from the weblet, as the minimal app does. If the browser must call the service, follow the design requirements. Do not remove the service's check, and do not treat CORS as the cause or the fix. The response is already open to every origin.
Wrong include target or type
Likely causes
- The identifier is misspelled, or its
.prefix was dropped. - The target is a service or weblet, but
includedefaulted toresource. - The target exists locally but has not been deployed.
includeresolves on the instance. - The script has no app namespace, so
.namecannot resolve.
How to confirm
The error names the type and identifier it looked for, as written in the include, for example Unable to load enhancement "resource:.api" in INCLUDE. Compare that with the manifest. A compile pass does not check this.
Fix
Correct the identifier, push the target, or set the type (Reference):
<include id=".api" type="service" />
Already-decoded JSON
Likely causes
The value came from an application/json resource, which include returns decoded. decode:json then receives an array instead of a string.
How to confirm
Check the resource's mimetype in the manifest. Inspect the value with typeof:
<include id=".defaults" var="defaults" />
<typeof var="defaults" var_result="kind" />
<output>$kind</output>
Fix
Delete the decode:json. The reverse problem also exists: a JSON request body arrives as a string, so read it with var_body and decode it inside try. See Includes and JSON resources.
Missing or empty session locale
Likely causes
- The code reads
$session.lang. The locale is in$session.data.lang(Source-confirmed). - There is no session user and no valid cookie, as in
TIMINGorzeysdk run. - The code runs in a hook or in
REMOTECALL, where the result depends on what triggered it. Do not rely onsessionthere.
How to confirm
Check the context. session returns NULL without a session (Reference). To check the value on a sandbox, output the named key, such as $session.data.lang, from a weblet.
Never dump the whole session array. It can include credential columns from the user record.
Fix
Use $AUTH.lang in REMOTECALL and always provide a fallback:
<session var="session" />
<set var="lang">$session.data.lang</set>
<if value1="$lang" value2="">
<set var="lang">en_US</set>
</if>
Empty lint selection
Symptom: zeysdk lint prints No modified iXML or Zymba files to lint. and exits 0.
Likely causes
- Nothing differs from the last synchronized state, for example straight after a push.
- The changed script is not registered in the manifest.
How to confirm
Run zeysdk lint --all and read the file list. Check that your file appears.
Fix
Compile the file directly with zeysdk compile <path>, or register it. Record an empty selection as no files checked, never as a pass.
Uncertain completion after a timeout
Symptom: Request timed out after 30000ms, or a dropped connection, during run or push.
Likely causes
The client stopped waiting. That shows nothing about the server, which may have finished, may still be running, or may have stopped. See Timeouts and uncertain completion.
How to confirm
Check the effects directly: records, mail, external systems, and the platform log. After a push, run zeysdk compare.
Fix
Do not retry blindly. The SDK's request layer does not resend these requests, so a second run is a second execution. zeysdk watch does retry a failed push on its own, so stop it first. Make long work idempotent and resumable, and move it to a TIMING service.
Text disappears from output
Symptom: <output> shows only part of what you wrote.
Cause: literal text used alongside an embedded statement is discarded (Reference). Logged on 6 February 2026: <output><set>$(15 + (7.5 * 2 - 3))</set>471</output> returned 27, not 27471.
Fix: assign to a variable first, then substitute it.
<!-- Wrong: "Hello" is discarded -->
<output>Hello <encode:html>$name</encode:html></output>
<!-- Right -->
<encode:html var="safeName">$name</encode:html>
<output>Hello $safeName</output>
See Result propagation.
replace changes nothing or the wrong text
Cause: in replace, value is the search value. The new text goes in replacement, and the subject is the element content (Reference).
<replace value="Bill Gates" replacement="William Henry Gates III">My name is Bill Gates!</replace>
A route works over HTTP but returns 404 in zeysdk run
Cause: this is expected. The console has no inbound route and its requests use PUT, so GET and POST routes do not match. Logged on 6 February 2026: HTTP 404 and null.
Fix: test the route with an HTTP request. See Checkpoint 4.
Reported, not independently verified — check in your target
These items come from an app build reported in November 2026. They were not reproduced for this guide, and none of them is established behavior. Test each one on your target before relying on either the report or the safe form.
| Report | What the reference says | How to check | Conservative form |
|---|---|---|---|
After assign, array:merge on the copy also changed the source array | Complex variables are assigned by reference, and array:merge is destructive unless var_result is set | Assign, merge, then print the source | Merge with var_result, or use clone |
JSON false and a missing key both read as an empty value | is separates defined, false, and empty. array:keyexists tests for a key | Decode {"a":false} and compare $data.a with $data.b | Use array:keyexists for presence and is for type |
No rounding inside $( ), and empty operands break arithmetic | math:round exists. Uninitialized variables default to the neutral literal | Compare math:round with an inline expression on sample values | Use math:round, and default empty operands to 0 first |
db:is with an empty value on an integer column fails | Not stated | On a sandbox, run a read with an empty value | Check the value with is type="empty" before adding the condition |
An embedded weblet with height 0 sizes itself to its content | Not stated | Register a test weblet and resize its content | Set an explicit height until confirmed on your target |