Spikard Troubleshooting Guide¶
This comprehensive guide covers common issues encountered when working with Spikard across OpenAPI, GraphQL, and Protobuf/gRPC protocols. Solutions are organized by problem category with clear error examples, root causes, and prevention strategies.
Table of Contents¶
- Schema Validation Errors
- Code Generation Failures
- Quality Validation Failures
- Integration Issues
- Runtime Errors
- gRPC-Specific Issues
Schema Validation Errors¶
Problem 1: OpenAPI - Missing Required Field in Request Body¶
Error Message:
{
"type": "https://spikard.dev/errors/validation-error",
"title": "Request Validation Failed",
"status": 422,
"detail": "1 validation error in request",
"errors": [
{
"type": "missing",
"loc": ["body", "username"],
"msg": "Field required",
"input": {}
}
]
}
Root Cause: The client is submitting a request missing a field marked as required in the OpenAPI schema. Spikard validates all incoming requests against the schema before they reach your handler.
Solution:
- Check your OpenAPI schema definition:
components:
schemas:
CreateUserRequest:
type: object
required:
- username # This field is mandatory
- email
properties:
username:
type: string
email:
type: string
- Ensure the client includes all required fields:
// TypeScript client
const response = await fetch('/users', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
username: 'johndoe', // Include required field
email: 'john@example.com'
})
});
Prevention Tips:
- Use OpenAPI documentation generators to share API contracts with clients
- Enable request validation in development to catch issues early
- Consider providing default values for optional fields in your schema
- Use schema examples in your OpenAPI spec to document expected inputs
Problem 2: GraphQL - Invalid Query Syntax¶
Error Message:
{
"errors": [
{
"message": "GraphQL parse error: Syntax error near line 3",
"extensions": {
"code": "GRAPHQL_PARSE_ERROR",
"status": 400,
"type": "https://spikard.dev/errors/graphql-parse-error"
}
}
]
}
Root Cause: The GraphQL query contains syntax errors such as mismatched braces, missing colons, or invalid field names.
Solution:
Check for common syntax issues:
# INCORRECT - missing closing brace
query GetUser {
user(id: "123") {
id
name
email
# CORRECT
query GetUser {
user(id: "123") {
id
name
email
}
}
Prevention Tips:
- Use GraphQL IDE tools (GraphiQL, GraphQL Playground) with syntax highlighting
- Enable query validation in your GraphQL client
- Use typed GraphQL clients (like Apollo Client with code generation)
- Add unit tests for your GraphQL queries
Problem 3: Protobuf - Message Type Not Found¶
Error Message:
Root Cause: The protobuf message type referenced in your code doesn't exist in the compiled descriptor set, usually due to incorrect imports or package names.
Solution:
- Verify your
.protofile package and message definitions:
syntax = "proto3";
package example.v1; // Package must match
message UserRequest {
string id = 1;
string name = 2;
}
- Ensure you're importing the correct generated code:
// TypeScript
import { UserRequest } from './gen/example/v1/user';
const request = UserRequest.create({ id: "123", name: "John" });
Prevention Tips:
- Use consistent package naming across all
.protofiles - Regenerate protobuf code after schema changes
- Verify import paths match your project structure
- Use
protoc --descriptor_set_outto debug descriptor issues
Problem 4: String Length Constraint Violation¶
Error Message:
{
"type": "https://spikard.dev/errors/validation-error",
"status": 422,
"errors": [
{
"type": "string_too_short",
"loc": ["body", "password"],
"msg": "String should have at least 8 characters",
"ctx": { "min_length": 8 }
}
]
}
Root Cause: Input string doesn't meet the minimum or maximum length constraints defined in the schema.
Solution:
- Check schema constraints:
- Validate input before submission:
# Python
def validate_password(password: str) -> bool:
if len(password) < 8:
raise ValueError("Password must be at least 8 characters")
if len(password) > 128:
raise ValueError("Password must not exceed 128 characters")
return True
Prevention Tips:
- Display constraint information in UI forms
- Implement client-side validation matching server rules
- Provide clear error messages indicating the constraints
- Use appropriate input types (e.g., password fields with length indicators)
Problem 5: Enum Value Not in Allowed List¶
Error Message:
{
"type": "enum",
"loc": ["body", "status"],
"msg": "Input should be 'active', 'inactive' or 'pending'",
"ctx": { "expected": "'active', 'inactive' or 'pending'" }
}
Root Cause: The provided value doesn't match any of the allowed enum values defined in the schema.
Solution:
- Review allowed enum values:
- Use type-safe enums in code:
// TypeScript
enum UserStatus {
Active = 'active',
Inactive = 'inactive',
Pending = 'pending'
}
const request = {
status: UserStatus.Active // Type-safe
};
# Python
from enum import Enum
class UserStatus(str, Enum):
ACTIVE = "active"
INACTIVE = "inactive"
PENDING = "pending"
request = {"status": UserStatus.ACTIVE.value}
Prevention Tips:
- Generate client code from schemas to get type-safe enums
- Use autocomplete-enabled dropdowns in UI
- Document enum values in API documentation
- Consider using constants instead of magic strings
Code Generation Failures¶
Problem 6: TypeScript - Module Resolution Failure¶
Error Message:
Root Cause: Generated TypeScript files aren't in the expected location or the module resolution configuration is incorrect.
Solution:
- Verify code generation succeeded:
- Check
tsconfig.jsonpaths configuration:
- Verify generated files exist:
Prevention Tips:
- Add generated code directories to
.gitignore - Use consistent output paths across the team
- Document code generation steps in README
- Add code generation to CI/CD pipeline
Problem 7: Ruby - Syntax Error in Generated Code¶
Error Message:
Root Cause: The code generator produced invalid Ruby syntax, often due to schema edge cases or special characters in field names.
Solution:
- Check for reserved keywords or special characters:
- Regenerate code with proper escaping:
- If the issue persists, report the schema pattern to the Spikard team.
Prevention Tips:
- Avoid using language-reserved keywords in schema field names
- Use
snake_casefor field names consistently - Run syntax validation immediately after generation
- Enable strict validation mode during generation
Problem 8: Python - Import Error for Generated Protobuf¶
Error Message:
Root Cause: The gRPC Python code wasn't generated or is out of sync with the .proto files.
Solution:
- Regenerate gRPC Python code:
python -m grpc_tools.protoc \
--proto_path=. \
--python_out=./gen \
--grpc_python_out=./gen \
user.proto
- Verify both
*_pb2.pyand*_pb2_grpc.pyfiles exist:
- Check import paths:
# Use relative imports if in package
from .gen import user_pb2, user_pb2_grpc
# Or absolute imports
from gen import user_pb2, user_pb2_grpc
Prevention Tips:
- Add protobuf compilation to build scripts
- Use consistent proto file organization
- Version control
.protofiles, not generated code - Document gRPC setup in project README
Quality Validation Failures¶
Problem 9: Python - mypy Type Error¶
Error Message:
Root Cause: The handler returns a plain dictionary instead of the typed response model, causing mypy strict mode to fail.
Solution:
Use proper type annotations and return types:
# INCORRECT
def get_user(user_id: str):
return {"id": user_id, "name": "John"}
# CORRECT
from msgspec import Struct
class UserResponse(Struct):
id: str
name: str
def get_user(user_id: str) -> UserResponse:
return UserResponse(id=user_id, name="John")
Prevention Tips:
- Enable mypy in pre-commit hooks
- Use
--strictmode during development - Generate type stubs from OpenAPI schemas
- Run type checking in CI/CD
Problem 10: TypeScript - tsc Compilation Error¶
Error Message:
Root Cause: Nullable fields in the schema aren't handled with proper type guards in TypeScript.
Solution:
Add proper null checks:
// INCORRECT
function processUser(user: User) {
const name: string = user.name; // user.name might be undefined
console.log(name.toUpperCase());
}
// CORRECT
function processUser(user: User) {
if (user.name) {
const name: string = user.name;
console.log(name.toUpperCase());
} else {
console.log("Name not provided");
}
}
// OR use optional chaining
function processUser(user: User) {
console.log(user.name?.toUpperCase() ?? "Name not provided");
}
Prevention Tips:
- Enable
strictNullChecksintsconfig.json - Use non-nullable types in schemas where appropriate
- Apply consistent null-handling patterns
- Use TypeScript 4.4+ for improved type narrowing
Problem 11: Ruby - Steep Type Mismatch¶
Error Message:
Root Cause: Optional fields in the schema produce nullable types in Ruby, requiring explicit nil handling.
Solution:
Use proper type signatures and nil checks:
# Add Steep type signature
# @type var user: User
# @type var name: String
user = get_user(id)
# INCORRECT
name = user.name
puts name.upcase
# CORRECT
if user.name
name = user.name
puts name.upcase
else
puts "Name not available"
end
Prevention Tips:
- Use RBS type definitions
- Enable Steep in CI pipeline
- Use Sorbet for runtime type checking
- Mark required fields explicitly in schemas
Problem 12: PHP - PHPStan Level Max Error¶
Error Message:
Root Cause: Generated PHP code lacks proper type hints, failing PHPStan's strict analysis.
Solution:
Ensure schema includes type information:
# OpenAPI schema
User:
type: object
properties:
id:
type: string
format: uuid
email:
type: string
format: email
age:
type: integer
minimum: 0
Generated code will include types:
<?php
class User
{
public function __construct(
public string $id,
public string $email,
public int $age,
) {}
}
Prevention Tips:
- Use PHP 8.1+ for property types
- Specify formats in OpenAPI schema
- Run PHPStan with
--level=maxlocally - Keep generated code in separate namespace
Problem 13: Rust - Clippy Warnings as Errors¶
Error Message:
Root Cause: Generated Rust code violates Clippy's complexity limits, often due to handlers with many parameters.
Solution:
Refactor to use request objects:
// INSTEAD OF
fn handle_user(
id: String,
name: String,
email: String,
age: i32,
// ... 8 parameters
) -> Result<Response> {
// handler
}
// USE
#[derive(Deserialize)]
struct UserRequest {
id: String,
name: String,
email: String,
age: i32,
}
fn handle_user(req: UserRequest) -> Result<Response> {
// handler
}
Prevention Tips:
- Group related parameters into structs
- Use the builder pattern for complex requests
- Run
cargo clippybefore committing - Configure Clippy to allow specific warnings if needed
Integration Issues¶
Problem 14: CORS Preflight Request Failing¶
Error Message (Browser Console):
Access to fetch at 'https://api.example.com/users' from origin 'http://localhost:3000'
has been blocked by CORS policy: Response to preflight request doesn't pass access
control check: No 'Access-Control-Allow-Origin' header is present.
Root Cause: The server doesn't respond to OPTIONS requests or doesn't include required CORS headers.
Solution:
Configure CORS middleware:
# Python
from spikard import App
app = App()
app.config.cors.enabled = True
app.config.cors.allowed_origins = ["http://localhost:3000"]
app.config.cors.allowed_methods = ["GET", "POST", "PUT", "DELETE"]
app.config.cors.allowed_headers = ["Content-Type", "Authorization"]
// TypeScript
import { App } from 'spikard';
const app = new App({
cors: {
enabled: true,
allowedOrigins: ['http://localhost:3000'],
allowedMethods: ['GET', 'POST', 'PUT', 'DELETE'],
allowedHeaders: ['Content-Type', 'Authorization'],
}
});
# Ruby
require 'spikard'
app = Spikard::App.new
app.config.cors.enabled = true
app.config.cors.allowed_origins = ['http://localhost:3000']
app.config.cors.allowed_methods = ['GET', 'POST', 'PUT', 'DELETE']
Prevention Tips:
- Enable CORS in development mode
- Use environment-specific CORS configurations
- Test with actual frontend URLs, not just curl
- Log CORS headers in development
Problem 15: Authentication Token Validation Failure¶
Error Message:
{
"type": "https://spikard.dev/errors/authentication-error",
"title": "Unauthorized",
"status": 401,
"detail": "Invalid or expired authentication token"
}
Root Cause: The JWT token is expired, malformed, or signed with wrong secret.
Solution:
- Verify token structure and expiration:
# Python
import jwt
from datetime import datetime
def verify_token(token: str) -> dict:
try:
payload = jwt.decode(
token,
key="your-secret-key",
algorithms=["HS256"]
)
# Check expiration
if payload.get('exp', 0) < datetime.now().timestamp():
raise ValueError("Token expired")
return payload
except jwt.InvalidTokenError as e:
raise ValueError(f"Invalid token: {e}")
- Include valid token in requests:
// TypeScript
const response = await fetch('/api/users', {
headers: {
'Authorization': `Bearer ${validToken}`
}
});
Prevention Tips:
- Use standard JWT libraries (don't roll your own)
- Set reasonable token expiration times
- Implement token refresh mechanisms
- Log authentication failures for debugging
- Use environment variables for secrets
Problem 16: Request Body Too Large¶
Error Message:
{
"type": "https://spikard.dev/errors/bad-request",
"title": "Request Entity Too Large",
"status": 413,
"detail": "Request body exceeds maximum size of 1MB"
}
Root Cause: The request payload exceeds the configured body size limit.
Solution:
- Increase body size limit if appropriate:
- Or use streaming for large uploads:
# Python
from spikard import UploadFile
async def upload_file(file: UploadFile):
# Stream directly to storage
await storage.save(file.filename, file.stream)
Prevention Tips:
- Set appropriate limits for your use case
- Use streaming for large file uploads
- Implement chunked uploads for very large files
- Document size limits in API documentation
Runtime Errors¶
Problem 17: Handler Timeout¶
Error Message:
{
"type": "https://spikard.dev/errors/internal-server-error",
"title": "Gateway Timeout",
"status": 504,
"detail": "Handler execution exceeded timeout of 30s"
}
Root Cause: The handler takes longer than the configured timeout to execute, usually due to slow database queries or external API calls.
Solution:
- Optimize slow operations:
# SLOW - sequential database calls
async def get_user_data(user_id: str):
user = await db.get_user(user_id)
posts = await db.get_posts(user_id)
comments = await db.get_comments(user_id)
return {"user": user, "posts": posts, "comments": comments}
# FAST - parallel database calls
import asyncio
async def get_user_data(user_id: str):
user, posts, comments = await asyncio.gather(
db.get_user(user_id),
db.get_posts(user_id),
db.get_comments(user_id)
)
return {"user": user, "posts": posts, "comments": comments}
- Or increase timeout for specific endpoints:
- Use background tasks for long operations:
from spikard import BackgroundTask
async def process_upload(file_id: str):
# Long-running processing
await heavy_processing(file_id)
async def upload_handler(file: UploadFile):
file_id = await save_file(file)
# Return immediately, process in background
return {
"id": file_id,
"status": "processing"
}, BackgroundTask(process_upload, file_id)
Prevention Tips:
- Profile handler performance
- Add database query indexes
- Use caching for frequently accessed data
- Implement request queuing for expensive operations
- Set realistic timeout values
Problem 18: Dependency Injection Failure¶
Error Message:
Root Cause: The DI container doesn't have a provider for a required dependency.
Solution:
Register providers before starting the app:
# Python
from spikard import App, Provide
class Database:
def __init__(self, url: str):
self.url = url
app = App()
# Register provider
@app.provide(Database)
def create_db():
return Database(url="postgresql://localhost/mydb")
# Use in handler
async def get_user(user_id: str, db: Database = Provide()):
return await db.query(f"SELECT * FROM users WHERE id = {user_id}")
// TypeScript
import { App, Provide } from 'spikard';
class Database {
constructor(private url: string) {}
}
const app = new App();
// Register provider
app.provide(Database, () => {
return new Database('postgresql://localhost/mydb');
});
// Use in handler
app.get('/users/:id', async (req, { db }: { db: Database }) => {
return await db.query(`SELECT * FROM users WHERE id = ${req.params.id}`);
});
Prevention Tips:
- Register all providers at application startup
- Use dependency interfaces for testability
- Validate DI configuration before deployment
- Document required dependencies
Problem 19: Streaming Response Connection Closed¶
Error Message (Logs):
Root Cause: The client disconnected before the streaming response completed, or the server encountered an error mid-stream.
Solution:
Handle connection errors gracefully:
# Python
from spikard import StreamingResponse
import asyncio
async def stream_data():
try:
for i in range(100):
yield f"data: {i}\n\n"
await asyncio.sleep(0.1)
except asyncio.CancelledError:
# Client disconnected
print("Client disconnected, cleaning up...")
await cleanup()
raise
except Exception as e:
# Other error during streaming
print(f"Streaming error: {e}")
yield f"event: error\ndata: {str(e)}\n\n"
async def sse_handler():
return StreamingResponse(
stream_data(),
media_type="text/event-stream"
)
Prevention Tips:
- Implement proper error handling in generators
- Add heartbeat messages to detect dead connections
- Set appropriate timeout for streaming responses
- Test with flaky network conditions
Problem 20: Memory Leak with WebSocket Connections¶
Error Message:
MemoryError: Unable to allocate memory for WebSocket buffers.
Currently tracking 10000 active connections.
Root Cause: WebSocket connections aren't properly closed, leading to accumulation of stale connections and memory exhaustion.
Solution:
Implement connection lifecycle management:
# Python
from spikard import WebSocket
import asyncio
active_connections = set()
async def websocket_handler(ws: WebSocket):
await ws.accept()
active_connections.add(ws)
try:
while True:
data = await ws.receive_text()
await ws.send_text(f"Echo: {data}")
except Exception as e:
print(f"WebSocket error: {e}")
finally:
# Always cleanup
active_connections.discard(ws)
await ws.close()
# Periodic cleanup
async def cleanup_stale_connections():
while True:
await asyncio.sleep(60)
for ws in list(active_connections):
if ws.client_state.disconnected:
active_connections.discard(ws)
// TypeScript
import { WebSocket } from 'spikard';
const activeConnections = new Set<WebSocket>();
async function websocketHandler(ws: WebSocket) {
activeConnections.add(ws);
try {
for await (const message of ws) {
await ws.send(`Echo: ${message}`);
}
} catch (error) {
console.error('WebSocket error:', error);
} finally {
activeConnections.delete(ws);
}
}
Prevention Tips:
- Set connection limits
- Implement heartbeat/ping-pong to detect dead connections
- Add connection timeout
- Monitor active connection count
- Close connections on server shutdown
gRPC-Specific Issues¶
Problem 21: Protobuf Serialization Error¶
Error Message:
grpc._channel._InactiveRpcError: <_InactiveRpcError of RPC that terminated with:
status = StatusCode.INVALID_ARGUMENT
details = "Failed to parse request proto: invalid wire type"
Root Cause: The client is sending a protobuf message that doesn't match the expected schema, often due to version mismatch between client and server.
Solution:
- Ensure client and server use same
.protofiles:
- Verify message creation:
# Python
import user_pb2
# INCORRECT - missing required fields
request = user_pb2.GetUserRequest()
# CORRECT
request = user_pb2.GetUserRequest(id="user_123")
# Or use dict syntax
request = user_pb2.GetUserRequest(**{"id": "user_123"})
- Check for breaking changes in
.proto:
// OLD
message UserRequest {
string id = 1;
}
// NEW - breaking change (field number changed)
message UserRequest {
string user_id = 1; // Changed field name AND number
}
// CORRECT - non-breaking change (field renamed but number kept)
message UserRequest {
string user_id = 1; // Renamed but kept field number
}
Prevention Tips:
- Version your
.protofiles - Never change field numbers
- Use reserved fields for deleted fields
- Test serialization round-trips
- Keep
.protofiles in a shared repository
Problem 22: gRPC Status Code UNAVAILABLE¶
Error Message:
grpc._channel._InactiveRpcError: <_InactiveRpcError of RPC that terminated with:
status = StatusCode.UNAVAILABLE
details = "failed to connect to all addresses"
Root Cause: The gRPC server is not running, the address is incorrect, or there's a network connectivity issue.
Solution:
- Verify server is running:
- Check connection configuration:
# Python
import grpc
from user_pb2_grpc import UserServiceStub
# INCORRECT - wrong port
channel = grpc.insecure_channel('localhost:50050')
# CORRECT
channel = grpc.insecure_channel('localhost:50051')
stub = UserServiceStub(channel)
- Add retry logic:
# Python
import grpc
from grpc import StatusCode
def call_with_retry(stub, request, max_retries=3):
for attempt in range(max_retries):
try:
return stub.GetUser(request)
except grpc.RpcError as e:
if e.code() == StatusCode.UNAVAILABLE:
if attempt < max_retries - 1:
time.sleep(2 ** attempt) # Exponential backoff
continue
raise
Prevention Tips:
- Use health check endpoints
- Implement connection pooling
- Add circuit breakers for resilience
- Monitor gRPC server metrics
- Use service discovery in production
Problem 23: gRPC Metadata Not Propagated¶
Error Message:
Root Cause: gRPC metadata (headers) aren't being properly set or accessed on the client or server side.
Solution:
- Set metadata on client:
# Python
import grpc
channel = grpc.insecure_channel('localhost:50051')
stub = UserServiceStub(channel)
# Add metadata
metadata = [
('authorization', 'Bearer token123'),
('x-request-id', 'req_456')
]
response = stub.GetUser(request, metadata=metadata)
# Ruby
require 'spikard/grpc'
request = Example::GetUserRequest.new(id: 'user_123')
metadata = {
'authorization' => 'Bearer token123',
'x-request-id' => 'req_456'
}
response = stub.get_user(request, metadata: metadata)
- Access metadata on server:
# Python
class UserService:
def GetUser(self, request, context):
# Access metadata
metadata = dict(context.invocation_metadata())
auth_token = metadata.get('authorization')
if not auth_token:
context.abort(grpc.StatusCode.UNAUTHENTICATED,
'Missing authorization header')
return user_pb2.User(id=request.id, name="John")
# Ruby
class UserServiceHandler < Spikard::Grpc::Handler
def handle_request(request)
auth_token = request.metadata['authorization']
unless auth_token
return Spikard::Grpc::Response.error(
'Missing authorization header'
)
end
# Process request
user = Example::User.new(id: request.payload.id, name: 'John')
Spikard::Grpc::Response.new(payload: Example::User.encode(user))
end
end
Prevention Tips:
- Document required metadata fields
- Validate metadata early in handlers
- Use interceptors for common metadata handling
- Log metadata for debugging
Problem 24: gRPC Streaming Deadlock¶
Error Message:
Root Cause: Bidirectional streaming has improper flow control, causing both sides to wait for each other.
Solution:
Implement proper streaming patterns:
# Python - INCORRECT (deadlock)
def bidi_stream(request_iterator):
responses = []
# Trying to read all requests before sending responses
for request in request_iterator:
responses.append(process(request))
# Never reaches here because client is waiting for response
for response in responses:
yield response
# CORRECT - interleaved reading and writing
def bidi_stream(request_iterator):
for request in request_iterator:
response = process(request)
yield response # Send immediately
// TypeScript - bidirectional streaming
async function* bidiStream(
requests: AsyncIterable<Request>
): AsyncIterable<Response> {
for await (const request of requests) {
const response = await processRequest(request);
yield response; // Stream responses as they're ready
}
}
Prevention Tips:
- Use unary or server/client streaming when possible
- Implement timeouts for streaming calls
- Test streaming with various message patterns
- Document expected message flow
Problem 25: gRPC Large Message Error¶
Error Message:
grpc._channel._InactiveRpcError: <_InactiveRpcError of RPC that terminated with:
status = StatusCode.RESOURCE_EXHAUSTED
details = "Received message larger than max (4194304 vs. 4194304)"
Root Cause: The gRPC message exceeds the default 4MB size limit.
Solution:
- Increase message size limits:
# Python - Server
server = grpc.server(
futures.ThreadPoolExecutor(max_workers=10),
options=[
('grpc.max_receive_message_length', 50 * 1024 * 1024), # 50MB
('grpc.max_send_message_length', 50 * 1024 * 1024),
]
)
# Python - Client
channel = grpc.insecure_channel(
'localhost:50051',
options=[
('grpc.max_receive_message_length', 50 * 1024 * 1024),
('grpc.max_send_message_length', 50 * 1024 * 1024),
]
)
- Or use streaming for large data:
// Instead of single large message
service FileService {
rpc Upload(FileUploadRequest) returns (FileUploadResponse);
}
// Use streaming
service FileService {
rpc Upload(stream FileChunk) returns (FileUploadResponse);
}
message FileChunk {
bytes content = 1;
int64 offset = 2;
}
Prevention Tips:
- Use streaming for large payloads
- Implement chunking for file transfers
- Set appropriate size limits
- Monitor message sizes in production
- Consider alternative protocols for very large data
General Debugging Tips¶
Enable Debug Logging¶
# Python
import logging
logging.basicConfig(level=logging.DEBUG)
app = App()
app.config.debug = True
# Ruby
require 'logger'
app = Spikard::App.new
app.config.logger = Logger.new(STDOUT, level: :debug)
Validate Schemas¶
# AsyncAPI
spikard validate-asyncapi asyncapi.yaml
# Protobuf syntax
protoc --descriptor_set_out=/dev/null user.proto
Test Generated Code¶
# Python
mypy --strict gen/
# TypeScript
tsc --noEmit
# Ruby
steep check
# PHP
phpstan analyse --level=max
# Rust
cargo clippy -- -D warnings
Check Version Compatibility¶
# Verify Spikard version
spikard --version
# Check for updates
spikard check-updates
# Validate compatibility
spikard validate-config
Getting Help¶
If you encounter an issue not covered in this guide:
- Check the Spikard documentation
- Search GitHub issues
- Review examples directory
- Ask in the community Discord
- Report bugs with minimal reproduction case
When reporting issues, include:
- Spikard version (
spikard --version) - Language and runtime version
- Complete error message
- Minimal schema that reproduces the issue
- Steps to reproduce