{"record":{"id":"837f6aafb40300b0","repo":"windmill-labs/windmill","slug":"backend-runnable-target-key-not-found-in-app","errorCode":null,"errorMessage":"Backend runnable \"${target.key}\" not found in app \"${appPath}\".","messagePattern":"Backend runnable \"(.+?)\" not found in app \"(.+?)\"\\.","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"frontend/src/lib/components/copilot/chat/global/core.ts","lineNumber":1772,"sourceCode":"}\n\nfunction getInlineScriptExtension(runnable: PersistedRunnable | undefined): 'ts' | 'py' {\n\treturn runnable?.inlineScript?.language === 'python3' ? 'py' : 'ts'\n}\n\n/**\n * Resolve a backend file target to its inline script body, validating that the\n * runnable exists, is inline, and matches the requested file extension. Throws\n * with a clear message otherwise.\n */\nfunction getInlineRunnableContent(\n\tvalue: AppDraftValue,\n\ttarget: { kind: 'backend'; filePath: string; key: string; extension: 'ts' | 'py' },\n\tappPath: string\n): { content: string; runnable: PersistedRunnable } {\n\tconst runnable = value.runnables[target.key] as PersistedRunnable | undefined\n\tif (!runnable) {\n\t\tthrow new Error(`Backend runnable \"${target.key}\" not found in app \"${appPath}\".`)\n\t}\n\tif (runnable.type !== 'inline' && runnable.type !== 'runnableByName') {\n\t\tthrow new Error(\n\t\t\t`Runnable \"${target.key}\" is not inline. Use read_workspace_item on the referenced ${runnable.runType ?? 'item'} instead.`\n\t\t)\n\t}\n\tconst expected = getInlineScriptExtension(runnable)\n\tif (target.extension !== expected) {\n\t\tthrow new Error(\n\t\t\t`Runnable \"${target.key}\" language is ${expected}. Use backend/${target.key}/main.${expected}.`\n\t\t)\n\t}\n\treturn { content: runnable.inlineScript?.content ?? '', runnable }\n}\n\nasync function loadAppValueForRead(path: string, workspace: string): Promise<AppDraftValue> {\n\tconst draft = await getGlobalDraft(workspace, 'app', path)\n\tif (draft && draft.value && typeof draft.value === 'object' && 'files' in draft.value) {","sourceCodeStart":1754,"sourceCodeEnd":1790,"githubUrl":"https://github.com/windmill-labs/windmill/blob/e474e8803ce2ff5c2df09a58dab51d45f5c922ca/frontend/src/lib/components/copilot/chat/global/core.ts#L1754-L1790","documentation":"When extracting a backend runnable's source from an app draft value, core.ts looks up value.runnables[target.key]. This error is thrown when no runnable exists under that key in the draft of the app at appPath, meaning the target key is stale, mistyped, or the app was changed by another step.","triggerScenarios":"Calling an app backend-file read/edit tool with key:\"fetch\" while the draft's runnables map only has keys like \"get_data\"; the runnable was deleted or renamed after the target was computed; the app path was redeployed and keys regenerated.","commonSituations":"The AI guesses backend runnable keys instead of reading the app structure first; concurrent edits removed the runnable; a copy of a target object from an earlier app version is reused.","solutions":["Re-read the app draft to list the actual runnable keys and use one of those.","Fix the key spelling/casing in the tool call.","If the runnable was removed, recreate it before editing its file.","Derive targets from fresh tool output each time rather than caching them across steps."],"exampleFix":"// before\nreadBackendFile({ appPath: 'f/app', key: 'fetch' }) // not in draft\n// after\nreadBackendFile({ appPath: 'f/app', key: 'get_data' }) // key from the app draft listing","handlingStrategy":"validation","validationCode":"const draft = await readAppDraft(appPath)\nif (!(target.key in draft.value.runnables)) {\n  throw new Error(`key ${target.key} not in ${Object.keys(draft.value.runnables)}`)\n}","typeGuard":"function hasRunnable(v: AppDraftValue, key: string): boolean {\n  return Object.prototype.hasOwnProperty.call(v.runnables ?? {}, key)\n}","tryCatchPattern":"try {\n  await readBackendFile({ appPath, key })\n} catch (e) {\n  if (e.message.includes('not found in app')) {\n    const draft = await readAppDraft(appPath)\n    const realKey = Object.keys(draft.value.runnables).find((k) => k === key || k.includes(key))\n    if (realKey) await readBackendFile({ appPath, key: realKey })\n  }\n}","preventionTips":["Re-read the app draft immediately before targeting backend runnables","Never reuse keys cached from an earlier version of the app","Match keys exactly (case-sensitive) from the runnables map, don't guess"],"tags":["lookup-failure","stale-state","apps","ai-tools"],"backgroundTag":"key-not-found","analyzedSha":"e474e8803ce2ff5c2df09a58dab51d45f5c922ca","analyzedAt":"2026-09-03T12:38:19.024Z","contentChangedAt":"2026-09-03T12:38:19.024Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}