Debugging
Logs
All custom channel pipelines log to procpiper.log. For debugging, it is useful to add Log pipes at key points in the pipeline — before and after HTTP requests, after unpacking the response, before registering events.
Example: logging around an HTTP request
[
{
"id": 1,
"type": "log",
"params": {
"message": "[Request] URL: $sender.url, method: POST",
"level": "debug"
},
"outs": {
"success": 2
}
},
{
"id": 2,
"type": "http_request",
"params": {
"method": "POST",
"timeout": 10,
"content_type": "json",
"url": "$sender.url",
"success_codes": [200]
},
"outs": {
"success": 3,
"error": 5
}
},
{
"id": 3,
"type": "unpack",
"outs": {
"success": 4,
"error": 5
}
},
{
"id": 4,
"type": "log",
"params": {
"message": "[Response] status: $in.data.status",
"level": "debug"
}
},
{
"id": 5,
"type": "log",
"params": {
"message": "[Error] code: $error.code, message: $error.message",
"level": "error"
}
}
]
Error data
When any pipe encounters an error, the following variables are available:
| Variable | Description |
|---|---|
$error.code | Error code (number). Not all pipes return a code — see the table below |
$error.message | Text description of the error. Available for all pipes |
Error structure by pipe
| Pipe | $error.code | $error.message |
|---|---|---|
| HTTP Request | Yes (HTTP code: 400, 403, 500, etc.) | Yes (error description: timeout, connection refused, etc.) |
| Pack | No | Yes (template error description) |
| Unpack | No | Yes (unmarshalling error description) |
| SQL | No | Yes (SQL error description) |
| Eventer | No | Yes (template or format error description) |
| Scheduler | No | Yes (template error description) |
| Selector | No | Yes (JsonQL error description) |
| Store Set / Store Get | No | Yes (database error description) |
| Log | No | Yes (template error description) |
| Result | No | Yes (path error or missing data description) |
| Error | No | Yes (message template error description) |
Use them in Log, Eventer, or Error pipes for diagnostics.
Typical errors
SQL: forgot map: true in Read operation
Symptom: Result fails with a data type error.
Cause: Without map: true, SQL returns an array []map[string]interface{}, but Result expects an object map[string]interface{}.
Solution: Add "map": true to the SQL pipe parameters for the Read operation.
Templating: $ instead of $#
Symptom: A numeric field arrived as a string, the external API returned a validation error.
Cause: The $ prefix always returns a string. The $# prefix preserves the original type.
Solution: Use $# for numbers, booleans, arrays, and objects. For example, $#template.discountAmount instead of $template.discountAmount.
Scheduler: did not pass send_message_id in template
Symptom: Eventer in the Schedule pipeline cannot register an event — $origin.send_message_id is missing.
Cause: In Schedule, $origin is replaced with params.template of the Scheduler pipe.
Solution: Pass send_message_id explicitly through the template:
{
"template": {
"send_message_id": "$origin.send_message_id",
"external_id": "$#in.data.externalId"
}
}
And access it in Schedule as $origin.send_message_id.
HTTP Request: missing Pack before content_type: json
Symptom: Error $in.body not found.
Cause: content_type: json reads the body from $in.body, which is formed by the Pack pipe.
Solution: Add a Pack pipe before HTTP Request in the chain.
HTTP Request: $in.body is not a byte array
Symptom: Data type error for $in.body.
Cause: Pack pipe is not configured or type is not specified.
Solution: Make sure the Pack pipe has "type": "JSON" (or "XML") and is placed before HTTP Request.
Eventer: incorrect channel_id / action_type
Symptom: Event does not appear in reports and profile history.
Cause: channel_id does not match the numeric ID of the channel, action_type does not match the Event ID of the created event.
Solution: Verify the correspondence in the Events section of the channel: action_type = Event ID of the event.
Selector: error in JsonQL
Symptom: Event goes to outs.error of the Selector pipe.
Cause: Incorrect JsonQL query syntax or field not found.
Solution: Check the query syntax and make sure the field exists in $in or $template.
Channel verification after creation
- Create a template with minimal content
- Create a sender and a subscription (or a resource for indirect)
- Send a test mailing
- Check
procpiper.log— entries from Log pipes should appear - Check events in profile history — a
sendorundelivevent should appear - If there is an error — add Log pipes before and after the problematic pipe to localize the failure point