11 KiB
11 KiB
Quick Reference: Action Output Format and Schema
Last Updated: 2026-02-07
Status: Current standard for all actions
TL;DR
- ✅ DO: Set
output_formatto "text", "json", or "yaml" - ✅ DO: Define
output_schemafor structured outputs (json/yaml only) - ❌ DON'T: Include stdout/stderr/exit_code in output schema (captured automatically)
- 💡 Output schema describes the shape of structured data sent to stdout
Output Format Field
All actions must specify an output_format field in their YAML definition:
name: my_action
ref: mypack.my_action
runner_type: shell
entry_point: my_action.sh
# Output format: text, json, or yaml
output_format: text # or json, or yaml
Supported Formats
| Format | Description | Worker Behavior | Use Case |
|---|---|---|---|
text |
Plain text output | Stored as-is in execution result | Simple messages, logs, unstructured data |
json |
JSON structured data | Parsed into JSONB field | APIs, structured results, complex data |
yaml |
YAML structured data | Parsed into JSONB field | Configuration, human-readable structured data |
Output Schema
The output_schema field describes the shape of structured data written to stdout:
- Only applicable for
output_format: jsonoroutput_format: yaml - Not needed for
output_format: text(no parsing occurs) - Should NOT include execution metadata (stdout/stderr/exit_code)
Text Output Actions
For actions that output plain text, omit the output schema:
name: echo
ref: core.echo
runner_type: shell
entry_point: echo.sh
# Output format: text (no structured data parsing)
output_format: text
parameters:
type: object
properties:
message:
type: string
# Output schema: not applicable for text output format
# The action outputs plain text to stdout
Action script:
#!/bin/bash
INPUT=$(cat)
MESSAGE=$(echo "$INPUT" | jq -r '.message // ""')
echo "$MESSAGE" # Plain text to stdout
JSON Output Actions
For actions that output JSON, define the schema:
name: http_request
ref: core.http_request
runner_type: python
entry_point: http_request.py
# Output format: json (structured data parsing enabled)
output_format: json
parameters:
type: object
properties:
url:
type: string
required: true
# Output schema: describes the JSON structure written to stdout
# Note: stdout/stderr/exit_code are captured automatically by the execution system
output_schema:
type: object
properties:
status_code:
type: integer
description: "HTTP status code"
body:
type: string
description: "Response body as text"
success:
type: boolean
description: "Whether the request was successful (2xx status)"
Action script:
#!/usr/bin/env python3
import json
import sys
def main():
params = json.loads(sys.stdin.read() or '{}')
# Perform HTTP request logic
result = {
"status_code": 200,
"body": "Response body",
"success": True
}
# Output JSON to stdout (worker will parse and store in execution.result)
print(json.dumps(result, indent=2))
if __name__ == "__main__":
main()
YAML Output Actions
For actions that output YAML:
name: get_config
ref: mypack.get_config
runner_type: shell
entry_point: get_config.sh
# Output format: yaml (structured data parsing enabled)
output_format: yaml
# Output schema: describes the YAML structure written to stdout
output_schema:
type: object
properties:
server:
type: object
properties:
host:
type: string
port:
type: integer
database:
type: object
properties:
url:
type: string
Action script:
#!/bin/bash
cat <<EOF
server:
host: localhost
port: 8080
database:
url: postgresql://localhost/db
EOF
Execution Metadata (Automatic)
The following metadata is automatically captured by the worker for every execution:
| Field | Type | Description | Source |
|---|---|---|---|
stdout |
string | Standard output from action | Captured by worker |
stderr |
string | Standard error output | Captured by worker, written to log file |
exit_code |
integer | Process exit code | Captured by worker |
duration_ms |
integer | Execution duration | Calculated by worker |
Do NOT include these in your output schema - they are execution system concerns, not action output concerns.
Worker Behavior
Text Format
Action writes to stdout: "Hello, World!"
↓
Worker captures stdout as-is
↓
Execution.result = null (no parsing)
Execution.stdout = "Hello, World!"
Execution.exit_code = 0
JSON Format
Action writes to stdout: {"status": "success", "count": 42}
↓
Worker parses JSON
↓
Execution.result = {"count": 42, "message": "done"} (JSONB)
Execution.stdout = '{"count": 42, "message": "done"}' (raw)
Execution.exit_code = 0
YAML Format
Action writes to stdout:
status: success
count: 42
↓
Worker parses YAML to JSON
↓
Execution.result = {"count": 42, "message": "done"} (JSONB)
Execution.stdout = "count: 42\nmessage: done\n" (raw)
Execution.exit_code = 0
Error Handling
Stderr Usage
- Purpose: Diagnostic messages, warnings, errors
- Storage: Written to execution log file (not inline with result)
- Visibility: Available via execution logs API endpoint
- Best Practice: Use stderr for error messages, not stdout
Example:
#!/bin/bash
if [ -z "$URL" ]; then
echo "ERROR: URL parameter is required" >&2 # stderr
exit 1
fi
# Normal output to stdout
echo "Success"
Exit Codes
- 0: Success
- Non-zero: Failure
- Captured automatically: Worker records exit code in execution record
- Don't output in JSON: Exit code is metadata, not result data
Pattern Examples
Example 1: Simple Text Action
# echo.yaml
name: echo
output_format: text
parameters:
properties:
message:
type: string
# echo.sh
#!/bin/bash
INPUT=$(cat)
MESSAGE=$(echo "$INPUT" | jq -r '.message // ""')
echo "$MESSAGE"
Example 2: Structured JSON Action
# validate_json.yaml
name: validate_json
output_format: json
parameters:
properties:
json_data:
type: string
output_schema:
type: object
properties:
valid:
type: boolean
errors:
type: array
items:
type: string
# validate_json.py
#!/usr/bin/env python3
import json
import sys
def main():
params = json.loads(sys.stdin.read() or '{}')
json_data = params.get('json_data', '')
errors = []
valid = False
try:
json.loads(json_data)
valid = True
except json.JSONDecodeError as e:
errors.append(str(e))
result = {"valid": valid, "errors": errors}
# Output JSON to stdout
print(json.dumps(result))
if __name__ == "__main__":
main()
Example 3: API Wrapper with JSON Output
# github_pr_info.yaml
name: github_pr_info
output_format: json
parameters:
properties:
repo:
type: string
required: true
pr_number:
type: integer
required: true
output_schema:
type: object
properties:
title:
type: string
state:
type: string
enum: [open, closed, merged]
author:
type: string
created_at:
type: string
format: date-time
Migration from Old Pattern
Before (Incorrect)
# DON'T DO THIS - includes execution metadata
output_schema:
type: object
properties:
stdout: # ❌ Execution metadata
type: string
stderr: # ❌ Execution metadata
type: string
exit_code: # ❌ Execution metadata
type: integer
result:
type: object # ❌ Actual result unnecessarily nested
After (Correct)
# DO THIS - only describe the actual data structure your action outputs
output_format: json
output_schema:
type: object
properties:
count:
type: integer
items:
type: array
items:
type: string
# No stdout/stderr/exit_code - those are captured automatically
Best Practices
-
Choose the right format:
- Use
textfor simple messages, logs, or unstructured output - Use
jsonfor structured data, API responses, complex results - Use
yamlfor human-readable configuration or structured output
- Use
-
Keep output schema clean:
- Only describe the actual data structure
- Don't include execution metadata
- Don't nest result under a "result" or "data" key unless semantic
-
Use stderr for diagnostics:
- Error messages go to stderr, not stdout
- Debugging output goes to stderr
- Normal results go to stdout
-
Exit codes matter:
- 0 = success (even if result indicates failure semantically)
- Non-zero = execution failure (script error, crash, etc.)
- Don't output exit code in JSON - it's captured automatically
-
Validate your schema:
- Ensure output schema matches actual JSON/YAML structure
- Test with actual action outputs
- Use JSON Schema validation tools
-
Document optional fields:
- Mark fields that may not always be present
- Provide descriptions for all fields
- Include examples in action documentation
Testing
Test Text Output
echo '{"message": "test"}' | ./action.sh
# Verify: Plain text output, no JSON structure
Test JSON Output
echo '{"url": "https://example.com"}' | ./action.py | jq .
# Verify: Valid JSON, matches schema
Test Error Handling
echo '{}' | ./action.sh 2>&1
# Verify: Errors to stderr, proper exit code
Test Schema Compliance
OUTPUT=$(echo '{"param": "value"}' | ./action.py)
echo "$OUTPUT" | jq -e '.status and .data' > /dev/null
# Verify: Output has required fields from schema
Common Pitfalls
❌ Pitfall 1: Including Execution Metadata
# WRONG
output_schema:
properties:
exit_code: # ❌ Automatic
type: integer
stdout: # ❌ Automatic
type: string
❌ Pitfall 2: Missing output_format
# WRONG - no output_format specified
name: my_action
output_schema: # How should this be parsed?
type: object
❌ Pitfall 3: Text Format with Schema
# WRONG - text format doesn't need schema
output_format: text
output_schema: # ❌ Ignored for text format
type: object
❌ Pitfall 4: Unnecessary Nesting
# WRONG - unnecessary "result" wrapper
echo '{"result": {"count": 5, "name": "test"}}' # ❌
# RIGHT - output the data structure directly
echo '{"count": 5, "name": "test"}' # ✅
References
- Action Parameter Handling - Stdin-based parameter delivery
- Core Pack Actions - Reference implementations
- Worker Service Architecture - How worker processes actions
See Also
- Execution API endpoints (for retrieving results)
- Workflow parameter mapping (for using action outputs)
- Logging configuration (for stderr handling)