Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.heylua.ai/llms.txt

Use this file to discover all available pages before exploring further.

The Core Problem

A common debugging mistake is deploying five times to fix the same bug, when a single log line plus one deployment would have shown you the root cause immediately. If you’re patching against test failures rather than the actual return shape, stop and add a console.log first — then deploy once.

The 5-Step Debug Loop

1

Add a console.log to the suspicious spot

Log the actual value — not a guess, the real thing:
async execute(input: any) {
  const results = await Data.search('articles', input.query, 5, 0.7);
  
  // ✅ Log the raw return value FIRST
  console.log('Data.search result:', JSON.stringify(results, null, 2));
  
  // Don't touch the rest of the code yet
  return results.map(entry => ({ id: entry.id, title: entry.title }));
}
Log the entire object so you see the actual shape, not what you assumed it would be.
2

Push to sandbox (not production)

lua push
Or use sandbox mode for even faster iteration — no push needed:
lua chat
# Select "Sandbox" when prompted
In sandbox mode, lua chat compiles and uses your local code directly. Use this for rapid iteration before committing to a push.
3

Send ONE test message

# Non-interactive: send a single message and exit
lua chat -m "search for thriller movies"
Or in interactive mode:
lua chat
# Type your test message, then Ctrl+C
Send the minimum message needed to trigger the tool. Don’t run a full conversation — you need one clean execution to inspect.
4

Check logs immediately

# See the most recent 10 skill executions
lua logs --type skill --limit 10

# Filter to a specific skill by name
lua logs --type skill --name my-skill --limit 5

# JSON output for piping/scripting
lua logs --type skill --json | head -100
Your console.log output appears in the log message body. Look for the 🔍 DEBUG or ℹ️ INFO entries — they contain your logged values.Example output:
🔍 [4/28/2026, 11:45:30 AM] DEBUG
   Skill Name: search-skill
   Skill ID:   skill_abc123
   Tool Name:  search_articles
   Data.search result: [
     {
       "id": "entry_xyz",
       "title": "Inception",
       "score": 0.92
     }
   ]
Now you can see the exact shape of what was returned and fix accordingly.
5

Fix once, not five times

You now know:
  • Exact shape of the return value
  • Which fields exist (and which don’t)
  • What the actual values look like
Fix the code, push once, verify with lua logs. Repeat until correct.

Suppressing CLI hints

All post-action hints can be silenced with:
LUA_NO_HINTS=1 lua push all --force --auto-deploy
Set LUA_NO_HINTS=true (or yes) in your shell profile or CI environment to disable hints globally. This is useful in CI/CD pipelines that capture only command output.

Reading lua logs Output

lua logs --type skill --limit 20

Log entry anatomy

✅ [4/28/2026, 11:45:30 AM] COMPLETE
   Skill Name: my-search-skill
   Skill ID:   skill_abc123
   Tool Name:  search_articles
   Duration:   234ms
   Execute function completed
FieldMeaning
Icon + timestampWhen the log was created
Log typeERROR, WARN, DEBUG, INFO, START, COMPLETE
Skill/Tool NameWhich component ran
DurationExecution time in ms
MessageThe actual log content (your console.log output)

Log types

TypeColorMeans
❌ ERRORRedTool threw an exception — check the stack trace
⚠️ WARNYellowNon-fatal issue, tool continued
🔍 DEBUGBlueconsole.log output from your tool code
ℹ️ INFOCyanOperational status messages
▶️ STARTGreenTool execution began
✅ COMPLETEGreenTool execution finished successfully

Filter by component type

# Skill tool execution
lua logs --type skill --limit 20

# Webhook execution
lua logs --type webhook --name stripe-webhook --limit 10

# Scheduled job execution
lua logs --type job --name daily-report --limit 5

# Preprocessor/Postprocessor
lua logs --type preprocessor --limit 10
lua logs --type postprocessor --limit 10

# User messages and agent responses
lua logs --type user_message --limit 20
lua logs --type agent_response --limit 20

# All logs
lua logs --type all --limit 50

When to Use lua test vs lua chat vs lua logs

GoalUse
Test a tool with exact input before touching the serverlua test
Test a conversational flow, verify the AI calls the right toolslua chat (sandbox)
See what actually ran in productionlua logs --type skill
Debug why an API call returned unexpected dataconsole.log + push + lua logs
Check if a job actually ran and what it returnedlua logs --type job --name my-job
Verify a webhook received and processed correctlylua logs --type webhook

console.log Debugging Patterns

Log a full API return value

const results = await Data.search('articles', input.query, 5, 0.7);
console.log('[DEBUG] Data.search result type:', typeof results, Array.isArray(results) ? `array[${results.length}]` : 'not array');
console.log('[DEBUG] Data.search result:', JSON.stringify(results, null, 2));

Log individual entries

const results = await Data.search('articles', input.query, 5);
results.forEach((entry, i) => {
  console.log(`[DEBUG] entry[${i}]:`, JSON.stringify({
    id: entry.id,
    score: entry.score,
    keys: Object.keys(entry.data || {}),
    sample: entry.title || entry.data?.title || '(no title field)'
  }));
});

Log before and after transformation

const raw = await Data.get('orders', { status: 'pending' });
console.log('[DEBUG] Data.get result — pagination:', JSON.stringify(raw.pagination));
console.log('[DEBUG] Data.get result — entry count:', raw.data.length);
if (raw.data.length > 0) {
  console.log('[DEBUG] First entry sample:', JSON.stringify(raw.data[0]));
}

const mapped = raw.data.map(entry => ({
  id: entry.id,
  orderId: entry.data.orderId,
  total: entry.data.total
}));
console.log('[DEBUG] After mapping:', JSON.stringify(mapped.slice(0, 2)));

Common Bugs and How to Spot Them

[DEBUG] Data.search result type: object array[3]
[DEBUG] Data.search result: [{"id":"entry_abc","data":{"title":"Inception"},"score":0.92}]
results.data would be undefined — there is no .data wrapper on the array. Use results[0].data.title for the entry payload, or results[0].title via the Proxy shortcut.

Bug: entry.title is undefined (Data.get)

[DEBUG] First entry sample: {"id":"entry_xyz","data":{"title":"The Matrix"}}
entry.title would be undefinedData.get entries are raw, not proxied. Use entry.data.title.
[DEBUG] Products.search result type: object
[DEBUG] Products.search result: {"products":[{"id":"prod_1","name":"Laptop"}]}
results.data doesn’t exist on ProductSearchInstance. Use results.products or results.map(p => p.name).

Removing Debug Logs Before Production

Once the bug is fixed, clean up your logs. Production logs are visible to your whole team and consume log storage.
// ❌ Don't leave these in production code
console.log('[DEBUG] raw result:', JSON.stringify(results));

// ✅ Leave meaningful operational logs
console.log(`Processed ${results.length} search results for query: "${input.query}"`);

lua logs Command

Full reference for the logs command and all filter options

lua test Command

Test tools locally before pushing

Data API

Data API return shapes reference

Troubleshooting

Common errors and solutions