Logs and Runtime Debugging
When page inspection reveals an unexpected result, agent-lynx can connect it to console logs, loaded scripts, and live JavaScript values. These provide evidence of where execution stopped and which runtime inputs the code received.
Trace an Unresponsive Tap to a Runtime Error
Return to the Tailwind CSS Settings example from page inspection. This time, tapping Ocean does not change the theme. To reproduce the failure, we added a missing-configuration read to the tap handler in a local checkout; the original example does not have this error.
The commands and outputs below were captured in Lynx Explorer on Android. The agent has already connected to the app and selected the page. $CLIENT_ID is the target app ID returned by list-clients; this page has session ID 2.
I tapped Ocean, but the theme did not change. Find out why.
Approach:I will find the Ocean button, start watching error logs, then tap it again to reproduce the problem.
agent-lynx snapshotOcean button found
# Read the current element refs. This excerpt shows the theme controls; @e15 is the Ocean button container.
agent-lynx snapshot --client "$CLIENT_ID" --session 2@e1 [scroll-view] @(207,450) {scrollable}
└─ @e2 [view] @(207,460)
├─ @e3 [view] @(207,70)
│ ├─ @e4 [text] "Settings" @(207,59)
│ └─ @e5 [text] "Manage your account and preferences." @(207,86)
├─ @e6 [view] @(207,177)
│ ├─ @e7 [view] @(207,153)
│ │ ├─ @e8 [text] "Theme" @(207,143)
│ │ └─ @e9 [text] "Switch design tokens at runtime via CSS variables." @(207,167)
│ └─ @e10 [view] @(207,207)
│ ├─ @e11 [view] @(91,206)
│ │ └─ @e12 [text] "Dark" @(91,206)
│ ├─ @e13 [view] @(207,206)
│ │ └─ @e14 [text] "Light" @(208,206)
│ └─ @e15 [view] @(324,206)
│ └─ @e16 [text] "Ocean" @(323,206)agent-lynx get-console --watchTwo errors captured
# Keep this watcher running while the next tap executes. These are the two errors from that tap; the watcher exits after receiving both.
agent-lynx get-console --watch --level error --limit 2 --client "$CLIENT_ID" --session 2[11:25:29.411Z] [error/background] The following error occurred in the JSRuntime:
at file://shared/lynx_core.js:2:10030
at file://shared/lynx_core.js:8:21708
at file://shared/lynx_core.js:8:21934
at file://shared/lynx_core.js:8:5388
at file://view3/.lynx/main/background.js:9055:27
[11:25:29.411Z] [error/background] TypeError: cannot read property 'ocean' of undefined
at <anonymous> (file://view3/.lynx/main/background.js:487:73)
at publishEvent (file://view3/.lynx/main/background.js:9054:27)
at file://shared/lynx_core.js:3:27
at file://shared/lynx_core.js:8:21708
at file://shared/lynx_core.js:8:21934
at file://shared/lynx_core.js:8:5388
at file://view3/.lynx/main/background.js:9055:27agent-lynx tap @e15Tap reproduced
# Tap the button from the current snapshot while the console watcher is listening, so the capture covers this action.
agent-lynx tap @e15 --client "$CLIENT_ID" --session 2Tapped @e15 at (324,206). Re-run `agent-lynx snapshot` before using refs again.Approach:The background VM threw a TypeError while reading ocean. The stack points to background.js; I will find that loaded script’s ID.
agent-lynx get-sourcesScript 5 matched
# List the scripts in the current background VM. app-service.js is present too; this error points to .lynx/main/background.js.
agent-lynx get-sources --client "$CLIENT_ID" --session 2[
{
"scriptId": "1",
"url": "file://shared/lynx_core.js"
},
{
"scriptId": "4",
"url": "file://view3/app-service.js"
},
{
"scriptId": "5",
"url": "file://view3/.lynx/main/background.js"
}
]agent-lynx cdp --method Debugger.getScriptSourceFailing callback found
# Read the actual source for script 5, then use jq to extract lines 484–491 of scriptSource around the failing callback.
agent-lynx cdp --thread background --method Debugger.getScriptSource '{"scriptId":"5"}' --client "$CLIENT_ID" --session 2 | jq -r '.scriptSource | split("\n")[483:491][]' $0: themeNames.map((t)=>/*#__PURE__*/ (0,_lynx_js_react_jsx_dev_runtime__rspack_import_0.jsxDEV)(__snapshot_835da_abf5c_2, {
values: [
`flex-1 rounded-md py-2 items-center justify-center border ${currentTheme === t ? "bg-primary border-primary" : "bg-secondary border-border"}`,
()=>setCurrentTheme(lynx.__globalProps.themeNames[t]),
`text-sm font-medium ${currentTheme === t ? "text-primary-foreground" : "text-secondary-foreground"}`
],
$0: t[0].toUpperCase() + t.slice(1)
}, t, false, {Approach:The callback reads lynx.__globalProps.themeNames[t] before calling setCurrentTheme. I will check themeNames in the same background VM.
agent-lynx evaluateField is undefined
# Read the configuration field used by the failing expression without changing page state.
agent-lynx evaluate 'lynx.__globalProps.themeNames' --thread background --client "$CLIENT_ID" --session 2{
"result": {
"type": "undefined"
}
}The tap reached its handler, which threw a TypeError while reading lynx.__globalProps.themeNames[t]. The current value of themeNames is undefined, so reading its ocean property fails before setCurrentTheme runs.
This example already gets the theme name from the iteration variable t; restore setCurrentTheme(t). If the application needs host configuration instead, define and supply that field and handle its absence explicitly.
The log connects the unchanged theme to an exception in the background VM. The script identifies the failing property read, and evaluate checks its current value. The script list includes app-service.js, while this stack points to .lynx/main/background.js; follow the URLs and script IDs returned by the runtime.
get-console subscribes to runtime logs; it does not guarantee access to past messages. This conversation starts --watch before tapping and ends the capture after two errors arrive. An empty short capture does not mean the page never logged an error.
Locate an Element's JSX and Event Handler
A stack trace identifies a location in the running script. A UI source map connects an element on the page to the JSX that created it. Looking up the Ocean button carries the investigation into the local source file.
This example runs the same bundle in a development client that supports DOM.getOriginalNodeSourceInfo, with UI source maps enabled at build time. $CLIENT_ID selects that client; this page has session ID 1.
Which JSX creates the Ocean button? Find its tap handler too.
Approach:I will take a fresh snapshot, select the Ocean button container, and map that element to its source.
agent-lynx snapshotOcean button found
# Refresh the page refs. This excerpt shows the theme controls; @e15 contains the Ocean label.
agent-lynx snapshot --client "$CLIENT_ID" --session 1@e1 [scroll-view] @(207,450) {scrollable}
└─ @e2 [view] @(207,460)
├─ @e3 [view] @(207,70)
│ ├─ @e4 [text] "Settings" @(207,59)
│ └─ @e5 [text] "Manage your account and preferences." @(207,86)
├─ @e6 [view] @(207,177)
│ ├─ @e7 [view] @(207,153)
│ │ ├─ @e8 [text] "Theme" @(207,143)
│ │ └─ @e9 [text] "Switch design tokens at runtime via CSS variables." @(207,167)
│ └─ @e10 [view] @(207,207)
│ ├─ @e11 [view] @(91,206)
│ │ └─ @e12 [text] "Dark" @(91,206)
│ ├─ @e13 [view] @(207,206)
│ │ └─ @e14 [text] "Light" @(208,206)
│ └─ @e15 [view] @(324,206)
│ └─ @e16 [text] "Ocean" @(323,206)agent-lynx get-source-info @e15App.tsx:62:15
# Resolve the button’s repository, source file, line, and column using the runtime node and UI source-map metadata.
agent-lynx get-source-info @e15 --client "$CLIENT_ID" --session 1{
"debugMetadataUrl": "http://127.0.0.1:33127/.lynx/main/debug-metadata.json",
"lazyBundleUrl": null,
"nodeId": 82,
"nodeIndex": 713223749,
"tag": "view",
"repo": "lynx-family/lynx-examples",
"source": "/tmp/agent-lynx-real-debugging-20261030/lynx-examples/examples/tailwindcss/src/App.tsx",
"line": 62,
"column": 15,
"status": "mapped",
"reason": null
}Approach:The mapping points to a view at App.tsx line 62. I will read the surrounding JSX to find the handler bound to that node.
Read · examples/tailwindcss/src/App.tsxbindtap found
// Read lines 61–80 of the local example checkout. Line 69 contains the failing handler introduced for this reproduction.
Read examples/tailwindcss/src/App.tsx:61–80 61 {themeNames.map((t) => (
62 <view
63 key={t}
64 className={`flex-1 rounded-md py-2 items-center justify-center border ${
65 currentTheme === t
66 ? "bg-primary border-primary"
67 : "bg-secondary border-border"
68 }`}
69 bindtap={() => setCurrentTheme(lynx.__globalProps.themeNames[t])}
70 >
71 <text
72 className={`text-sm font-medium ${
73 currentTheme === t
74 ? "text-primary-foreground"
75 : "text-secondary-foreground"
76 }`}
77 >
78 {t[0].toUpperCase() + t.slice(1)}
79 </text>
80 </view>The Ocean button maps to the view at line 62, column 15 of examples/tailwindcss/src/App.tsx, created by themeNames.map. Its bindtap at line 69 binds the handler that reads the missing configuration field identified above.
get-source-info returns the element's source location. Reading that file reveals the event binding and application logic. A mapped status indicates success; runtime-generated wrappers or pages without the required metadata may not resolve to source locations.
Keep Runtime Evidence on the Same Target
A session can contain both background and main VMs. The example's log is tagged background, so the subsequent script request and evaluation target that VM. get-sources lists scripts from the background VM.
Script IDs and remote object IDs belong to their client, session, and VM. For example, when a log contains an objectId, cdp can inspect its properties through Runtime.getProperties on the same target. After reloading or switching pages, obtain new logs and script IDs before continuing.
Logs and Runtime Debugging Capabilities
Filtering options, output formats, VM selection, and evaluation parameters are covered in the CLI reference. For component props, state, and relationships, continue with ReactLynx components.