Handling Undefined Response Types in AWS SDK v3 with TypeScript
AWS SDK v3 returns response objects with optional properties typed as T | undefined, requiring strict null checks and defensive programming to prevent runtime errors when accessing potentially missing fields.
AWS SDK v3 is fully modular, shipping as individual client packages like @aws-sdk/client-s3 where response fields are omitted when empty. When developing with the aws/agent-toolkit-for-aws repository, which compiles TypeScript with strict null checks enabled in the skills/ folder, you must handle these potentially undefined values to avoid runtime exceptions like Cannot read property of undefined.
Why SDK v3 Uses Optional Types
Unlike previous SDK versions, AWS SDK v3 generates TypeScript definitions that mark response properties as optional when the AWS API may omit them. For example, S3Client returns GetObjectOutput where Body?: ReadableStream | Uint8Array | Blob | string. This design accurately reflects service behavior but requires developers to verify field existence before accessing nested properties or calling methods.
Enable Strict Null Checks
The aws/agent-toolkit-for-aws repository enforces strict type-checking across its skill modules. Configure your tsconfig.json to catch undefined errors at compile time:
{
"compilerOptions": {
"strict": true,
// or specifically:
"strictNullChecks": true
}
}
This configuration, as implemented in the skills/ folder of the repository, forces TypeScript to treat undefined as a distinct type that must be explicitly narrowed or guarded.
Guard Against Undefined Values
Always verify optional properties exist before using them. The following pattern demonstrates safe handling of the optional Body property in S3 GetObject responses:
import { S3Client, GetObjectCommand, GetObjectOutput } from "@aws-sdk/client-s3";
export async function getObjectText(bucket: string, key: string): Promise<string> {
const client = new S3Client({ region: "us-east-1" });
const output: GetObjectOutput = await client.send(new GetObjectCommand({ Bucket: bucket, Key: key }));
// Guard against a missing body
if (!output.Body) {
throw new Error(`Object ${key} in bucket ${bucket} has no body`);
}
// Convert the readable stream to a string
const stream = output.Body as ReadableStream;
const chunks: Uint8Array[] = [];
const reader = stream.getReader();
while (true) {
const { done, value } = await reader.read();
if (done) break;
if (value) chunks.push(value);
}
const decoder = new TextDecoder("utf-8");
return decoder.decode(Buffer.concat(chunks));
}
Type Predicates for Narrowing
Create reusable type guards to narrow types safely. The hasProp helper checks if a property is defined and narrows the TypeScript type accordingly:
/**
* Generic guard that checks a property is not undefined.
* Usage: if (hasProp(resp, 'Body')) { … }
*/
export function hasProp<T, K extends keyof T>(obj: T, prop: K): obj is T & Record<K, NonNullable<T[K]>> {
return obj[prop] !== undefined;
}
// Example usage with SDK responses
if (hasProp(output, "Body")) {
// `output.Body` is now known to be defined
const stream = output.Body;
}
Safe Pagination Handling
Paginated operations like ListObjectsV2Command may return undefined for page contents. Use the nullish coalescing operator to provide safe defaults:
import { S3Client, paginateListObjectsV2 } from "@aws-sdk/client-s3";
export async function listAllKeys(bucket: string): Promise<string[]> {
const client = new S3Client({ region: "us-east-1" });
const keys: string[] = [];
for await (const page of paginateListObjectsV2({ client }, { Bucket: bucket })) {
// `Contents` may be undefined – default to an empty array
const objects = page.Contents ?? [];
for (const obj of objects) {
if (obj.Key) keys.push(obj.Key);
}
}
return keys;
}
Repository Context and File Locations
These patterns align with the Agent Toolkit for AWS architecture, where skills invoking AWS services follow strict TypeScript configurations. Key files demonstrating this integration include:
README.md– Provides the overall repository overview and packaging context for agent skillsplugins/aws-core/skills/aws-serverless/SKILL.md– Describes how TypeScript modules import and execute AWS SDK clients within serverless skillsskills/core-skills/aws-serverless/references/lambda.md– Contains examples of invoking AWS services from Lambda execution environmentsplugins/aws-core/skills/aws-sdk-swift-usage/SKILL.md– Demonstrates the modular client pattern that mirrors the TypeScript SDK architectureplugins/aws-data-analytics/skills/storing-and-querying-vectors/SKILL.md– Shows data service implementations where SDK clients handle optional response fields
Summary
- Enable strict null checks in
tsconfig.jsonto catch undefined errors at compile time - Guard every optional property before accessing nested values, particularly with
GetObjectOutput.Body - Use type predicates like
hasPropto narrow types and satisfy TypeScript's control-flow analysis - Apply nullish coalescing (
??) to paginated results to ensure arrays are always defined - Reference the repository's
skills/folder structure when implementing SDK calls in Agent Toolkit skills
Frequently Asked Questions
How do I handle the Body property in S3 GetObject responses?
The Body property in GetObjectOutput is typed as ReadableStream | Uint8Array | Blob | string | undefined. Always check if (!output.Body) before streaming, or use a type guard function to narrow the type. Attempting to access Body without checking results in a compile-time error under strict null checks or a runtime Cannot read property of undefined error.
Why does AWS SDK v3 mark response fields as optional?
AWS SDK v3 reflects the actual AWS API behavior where services omit fields that have no value rather than returning null. This modular approach generates TypeScript definitions with property?: T syntax, making undefined a valid state that developers must explicitly handle before accessing properties.
What is the best way to handle paginated responses that might be empty?
Use the nullish coalescing operator (??) to provide default empty arrays for optional paginated lists. For example, when using paginateListObjectsV2, write const objects = page.Contents ?? [] to ensure your iteration logic never receives undefined, as shown in the skills/core-skills/aws-serverless/references/lambda.md patterns.
Where can I find examples of TypeScript SDK usage in the Agent Toolkit?
The aws/agent-toolkit-for-aws repository contains relevant examples in plugins/aws-core/skills/aws-serverless/SKILL.md and skills/core-skills/aws-serverless/references/lambda.md. These files demonstrate how SDK clients are instantiated and used within the toolkit's skill architecture, following the strict TypeScript configuration defined in the skills/ folder.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →