From 5ed8984750137d5121126f64b274fea8fcc0c306 Mon Sep 17 00:00:00 2001 From: martin-ai Date: Tue, 9 Jun 2026 12:18:17 +0000 Subject: [PATCH 1/2] plugin docs: stdin/stdout uses type:"body" inside callbacks (not URL form) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The "Format of manifest.yml" example showed `stdin.url: "%_input.url%"` and `stdout.url: "%_output.url%"` inside the `callbacks:` block. That syntax is correct for `extensions:` (which expose the input/output as HTTP endpoints via the on-the-fly /reader and /writer mechanisms) but not for callbacks. In the callback dispatch path the placeholder is not substituted, and fylr attempts to use the literal "%_input.url%" as the URL to GET stdin from. The resulting `parse "%_input.url%": invalid URL escape "%_i"` error is what a plugin developer building a `db_pre_save` callback hit during local testing. Update both `transition_db_pre_save` and `db_pre_save` examples to use `type: "body"` (matching the IUCN plugin's `manifest.yml` and the dot.yml cookbook). Add a hint near the start of the Callbacks section explicitly calling out the extension-vs-callback transport difference and quoting the actual error message so the next person who hits it can search for it. The `extensions:` example at the top of the file is left unchanged — that one was already correct. --- for-developers/plugin.md | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/for-developers/plugin.md b/for-developers/plugin.md index 2f3468f..7d75521 100644 --- a/for-developers/plugin.md +++ b/for-developers/plugin.md @@ -64,9 +64,9 @@ callbacks: commands: - prog: "node" stdin: - url: "%_input.url%" + type: "body" stdout: - url: "%_output.url%" + type: "body" args: - type: "value" value: "set_comment.js" @@ -86,9 +86,9 @@ callbacks: commands: - prog: "node" stdin: - url: "%_input.url%" + type: "body" stdout: - url: "%_output.url%" + type: "body" args: - type: "value" value: "set_comment.js" @@ -152,6 +152,10 @@ In case of an error, fylr tries to parse errors using the api error format. If a Callbacks are predefined hooks in the API of FYLR. Each hook is different and might use its own format for in and output of the data. +{% hint style="warning" %} +**Stdin/stdout transport differs between extensions and callbacks.** Extensions use the URL-replacement form (`stdin.url: "%_input.url%"` / `stdout.url: "%_output.url%"`) because fylr exposes the input/output as on-the-fly HTTP endpoints. Callbacks (e.g. `db_pre_save`, `transition_db_pre_save`) connect the plugin's process stdin/stdout directly via `type: "body"`. Using the URL form inside a callback definition fails at job dispatch with `Could not prepare stdin "%_input.url%": parse "%_input.url%": invalid URL escape "%_i"` because the placeholder is not substituted in that path. +{% endhint %} + Errors can be generated using the api error JSON format which looks like this: ```json From 884b6fc53ebf7fd9c4af2fb8700ac5ad64721f09 Mon Sep 17 00:00:00 2001 From: martin-ai Date: Tue, 9 Jun 2026 12:27:42 +0000 Subject: [PATCH 2/2] plugin docs: document %_exec.pluginDir% for script paths in callbacks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The "Format of manifest.yml" callback examples referenced their script files by bare relative paths (`value: "set_comment.js"`). At runtime fylr's execserver runs the plugin process with cwd set to the per-job working directory under `tempDir` — *not* the plugin's directory — so the runtime can't find the script and fails with the language-specific "module not found" error (Node.js: `MODULE_NOT_FOUND` from `Module._load`; Python: `FileNotFoundError`; etc.). The fix is the `%_exec.pluginDir%` replacement, which already exists in `internal/server/execserver/exec.go:130` and is used in working real-world plugins (e.g. `easydb-custom-data-type-iucn/manifest.yml`: `value: "%_exec.pluginDir%/src/server/main-fylr.py"`). It was just never documented on the plugin doc page. Two changes: 1. Add `%_exec.pluginDir%` to the replacement table near the top of the page, with a one-line explanation of *why* you need it (so readers don't have to hit the runtime error first to learn the rule). 2. Update the two callback example blocks to prefix their script paths with `%_exec.pluginDir%/`, matching what an actually-working plugin looks like. The extension example at the top of the file already used absolute referencing for `%info.json%`, so the change there is just adding the prefix on the script-file `value`. --- for-developers/plugin.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/for-developers/plugin.md b/for-developers/plugin.md index 7d75521..875056b 100644 --- a/for-developers/plugin.md +++ b/for-developers/plugin.md @@ -22,6 +22,7 @@ Configured callbacks use replacements for specific URL to receive and send data. | `%_input.url%` | The URL pointing to an HTTP endpoint sending out the input data for the plugin. This can be mapped to STDIN or read directly by the plugin. | | `%_output.url%` | The URL to write the data back to. This can be mapped to STDOUT or written directly by the plugin. | | `%info.json%` | A map containing context information about the current callback. This includes the URL requested, its query in a parsed form, as well as the HTTP Headers of the request. In addition it includes the plugin's base config as returned by `/api/config`. Since version 6.17, the info also includes the languages as configured in the base config. Individual callbacks may add additional information. | +| `%_exec.pluginDir%` | The absolute filesystem path to the plugin's directory. Use this to reference scripts and other plugin resources by their absolute path — e.g. `value: "%_exec.pluginDir%/src/handler.js"`. Without this prefix the path is resolved relative to the per-job working directory (a transient subdirectory under fylr's `tempDir`), where your script does not exist; the process exits with the runtime's "module not found" error. | When a plugin wants to return an error, it needs to exit with a non zero exit code. If called as an extension plugin, FYLR sets the `X-Execserver-Error` header, unless more than 4K of data have already been produce and sent out to the response body. @@ -69,7 +70,7 @@ callbacks: type: "body" args: - type: "value" - value: "set_comment.js" + value: "%_exec.pluginDir%/set_comment.js" db_pre_save: steps: @@ -91,7 +92,7 @@ callbacks: type: "body" args: - type: "value" - value: "set_comment.js" + value: "%_exec.pluginDir%/set_comment.js" - type: "value" value: "%info.json%"