Migrating Hive Tables To S3 Tables
AWSOfficialPublished by AWS
- Skill
- Workflow
- Cloud and DevOps
- Apache-2.0
- No issues found
AutoGineer summary
Written by AutoGineer from the skill’s own files, not by the author
This skill provides a structured workflow for migrating existing Glue Data Catalog Hive Parquet tables into Amazon S3 Tables as managed Iceberg tables. It utilizes the native S3 Tables migration API to handle planning, data copying, and synchronization. The process includes rigorous pre-migration guardrails, plan review, and post-migration validation to ensure data integrity and compatibility before final cutover.
What it does
- Performs pre-migration compatibility checks on source Glue Hive tables.
- Generates and reviews migration plans for schema and object compatibility.
- Executes the migration process using native S3 Tables API calls.
- Synchronizes the destination table with the source until final cutover.
- Validates row counts and sample data parity between source and destination.
- Promotes the destination table to a read-write state upon successful validation.
When to use it
- When migrating a Glue Data Catalog Hive Parquet table to S3 Tables.
- When you need to keep the destination table in sync with the source before cutover.
- When you want to use the native S3 Tables migration service rather than manual ETL jobs.
- When you need to verify compatibility and plan the migration before execution.
Advantages
- Uses native, managed service calls for reliable table migration.
- Includes built-in validation steps to ensure data parity.
- Supports incremental synchronization to minimize downtime.
- Provides clear guardrails to prevent common configuration errors.
Usage examples
- “Migrate my Glue Hive table sales_data into my table bucket sales-bucket.”
- The agent will initiate the pre-migration checks, generate a migration plan, and present it for your approval.
- “Keep my migrated table in sync with the source before cutover.”
- The agent will execute the SyncTableMigration API to apply any recent changes from the source to the destination.
- “Complete my migrated table.”
- The agent will perform final validation and promote the destination table to read-write status.
Install
Download the skill as a ZIP:
https://mp-staging.autogineer.com/agent-skills/aws/migrating-hive-tables-to-s3-tables/downloadUnzip it into the cross-client skills folder of your project:
mkdir -p .agents/skills && unzip migrating-hive-tables-to-s3-tables.zip -d .agents/skills
Documentation by AWS
Migrating Hive Tables to S3 Tables
Contents
Overview
Before you migrate
When to use
When NOT to use (use another skill)
Scope and constraints
The 6 migration APIs
Migration lifecycle
Pre-migration guardrails
Post-migration guardrails
Key API shapes
Security considerations
References
Overview
This skill guides an agent through migrating one Glue Hive Parquet table into an S3 table bucket with the native S3 Tables migration API. Amazon S3 Tables plans the migration, reports column and object compatibility, copies the data, and keeps the destination in sync with the source until you complete it. Your coding agent, following this skill, runs preflight checks, starts the migration, presents the plan for approval, monitors progress, completes the migration, and validates the result.
When the AWS MCP Server is available, use it to load this skill and its references
(aws___retrieve_skill, with file="references/<name>.md") and to run the AWS calls.
Before you migrate
Agent instruction: You MUST present the migration plan and obtain explicit user approval
before any step that changes tables, data, metadata, or catalog entries, and before any IAM, KMS,
or S3 bucket change (creating or editing a role or policy, editing a key policy, creating a report
bucket). Show the exact JSON or command first. For a key policy, read it with get-key-policy,
add one statement, and never replace the whole policy. The plan MUST list:
the source table and the destination table bucket, namespace, and table name;
the write paths that will change (ETL jobs, streaming or ingest,
INSERT/MERGE, CDC);the query engines and downstream consumers that read the table;
the datasets, saved queries, and permissions that reference the old Glue table or S3 prefix.
Up-front approval in the user's request (naming the source, destination, and intent to migrate)
covers only read-only checks and a Start with plan review. Approving the plan, accepting any
setNull or drop policy, Complete, and any delete always need explicit approval during the
conversation. If the actual state differs from what was approved (a different schema,
partition layout, row count, or resource), stop, explain the difference, and get approval again.
Confirm these constraints with the user as part of the plan:
Every writer to the destination writes through an Iceberg-aware engine (for example Athena, EMR or Glue with Iceberg). Writers that write Parquet files directly to S3 will not work.
Every reader supports Iceberg and the S3 Tables catalog.
Jobs and queries move from the Glue Hive table to the S3 Tables catalog reference.
The source is not changed. Keep writers on the source until the destination is validated.
Validate row counts and sample values (references/validation.md) before moving production traffic.
See Migrating tables to S3 Tables for the full list of supported sources and limits.
Runtime: works with or without the AWS MCP server; steps use AWS CLI syntax. The AWS MCP server is recommended for sandboxed execution and audit logging. The migration commands are part of
aws s3tables; if an older CLI lacks them, see references/api-setup.md.
When to use
Use this skill when the source is a Glue Data Catalog Hive table with Parquet data
(TableType=EXTERNAL_TABLE, Hive Parquet SerDe) and the destination is an S3 table bucket in the
same account and Region:
Migrating that table into S3 Tables as managed Iceberg with the native migration API.
Keeping the migrated table in sync with the active Hive source before cutover.
When NOT to use (use another skill)
The names below are other agent skills. For these requests, do not use this skill; load the named skill instead. When asked when not to use this skill, list every case:
A self-run CTAS, Glue ETL, or EMR/Spark migration into Iceberg: use the
ingesting-into-data-lakeskill (this skill covers only the managed migration API).Converting a self-managed or standard Iceberg table: use the
ingesting-into-data-lakeskill.Loading raw files (Parquet with no Glue table, CSV, JSON) or other sources (JDBC, Snowflake, BigQuery, DynamoDB): use the
ingesting-into-data-lakeskill.Creating a new empty Iceberg table: use the
creating-data-lake-tableskill.Running or managing Athena queries: use the
querying-data-lakeskill.Locating tables or assets: use the
finding-data-lake-assetsskill. Auditing the catalog: use theexploring-data-catalogskill.Setting up Glue connections: use the
connecting-to-data-sourceskill.Any destination other than S3 Tables.
Prompt patterns (trigger phrasings)
"Migrate my Glue Hive table
<db>.<tbl>into my table bucket<name>.""Import an existing Hive table into S3 Tables using the native migration API."
"Move my Parquet Hive table to Iceberg without running CTAS or a Glue/Spark job."
"Use StartTableMigration to convert
<tbl>.""Keep my migrated table in sync with the source before cutover" → Sync.
"Complete my migrated table" → Complete.
"Check the status or plan of migration
<id>" → Get."List my table migrations" → List.
Scope and constraints
Source: Glue Hive Parquet table in the same account and region as the destination table bucket.
Destination: an existing S3 table bucket and namespace; the migration creates the table.
Not supported (confirm the current list in the S3 Tables migration guide): Lake Formation registered tables, partitions outside the table location, non-Parquet formats, Iceberg sources, cross-account, SSE-C, and archived storage classes. Check these in preflight and stop if any apply. For a Lake Formation registered table, tell the user it is not supported and suggest contacting AWS Support or their account team with the migration ID to request support. Never change Lake Formation settings.
Compatibility problems are reported during planning. Always review the plan before approving.
All-or-nothing copy: if a single object fails, the whole migration fails. A
succeededrun has no failed objects.Three reports are written to a general-purpose bucket you supply: table compatibility, object compatibility, and object migration.
Cost and duration: the migration writes a second copy of the data; plan for roughly double storage until the source is decommissioned. Large tables can take hours.
Quotas: see S3 Tables quotas.
Migration policies: by default an incompatible column fails the run.
incompatibleValuesPolicy: setNullandunmatchedSourceFieldsPolicy: dropapply to the whole column and the run still reportssucceeded. Treat anysetNullordroppedcolumn as data loss that needs explicit approval.
The 6 migration APIs
| API | Purpose | Phase |
|---|---|---|
StartTableMigration | Plan and begin a migration. With --review-migration-plan, pauses at pendingReview. | Start |
GetTableMigration | Returns state, hasValidPlan, planningStats, migrationStats, migrationOutput, failureReason. | Track, review |
UpdateTableMigration | Approve (--requested-state queued) or cancel. | Approve |
SyncTableMigration | Apply source changes since the last plan; repeat until current. | Sync |
CompleteTableMigration | Promote the destination to read-write. Takes --table-arn. Returns an empty body; the migration state stays succeeded. Confirm with get-table (managedByService is null). | Complete |
ListTableMigrations | List migrations for one table bucket or table. | Discover |
Request and response shapes are in references/lifecycle.md.
Migration lifecycle (phases)
Preflight → references/preflight.md, references/iam-roles.md
Start and plan:
start-table-migration --review-migration-plan;planning→pendingReview.Review:
get-table-migration; read the compatibility reports and present the plan for approval.Approve and migrate:
update-table-migration --requested-state queued; pollmigrating→succeeded.Sync (optional):
sync-table-migration; repeat until current.Validate: on the
succeededtable, before Complete, query the destination in Athena and compare with the source → references/validation.md. Validation is part of every migration: run the row-count and sample checks without being asked, unless the user opts out. If counts are short, reconcile withsync-table-migration(not possible after Complete).Complete: once parity holds and the user approves,
complete-table-migration --table-arn ..., then confirmget-tableshowsmanagedByServicenull.
Phases 2 to 7 are in references/lifecycle.md.
Pre-migration guardrails (run every time, before Start)
Do not call start-table-migration until all of these pass. Commands are in
references/preflight.md and references/iam-roles.md.
Plan approved. The plan in Before you migrate has been presented and approved.
Credentials and region.
sts get-caller-identitysucceeds; the source and destination are in the same account and region.Source is Glue Hive Parquet.
TableType=EXTERNAL_TABLE, Hive Parquet format and SerDe; not Iceberg, not ACID/transactional, no storage handler. RecordcatalogId.Partition locations. Every partition location is under the table location; partitions outside it fail planning with
UNSUPPORTED_PARTITIONS. Check for partition projection andskip.header.line.count.Lake Formation. If the source table or its location is registered with Lake Formation, stop: not supported (confirm current support in the migration guide). Do not change Lake Formation settings.
Destination exists. The table bucket and namespace exist. A missing namespace fails Start with
NotFoundException.Migration role trust. One statement, principal
migration.s3tables.amazonaws.com, conditionsaws:SourceAccountandaws:SourceArn.Migration role permissions. Scoped source S3 and Glue read, destination write, report bucket write. The caller also needs
iam:PassRoleon the role, conditioned oniam:PassedToService=migration.s3tables.amazonaws.com.KMS. If data is encrypted with a customer managed key, grant
kms:Decryptandkms:GenerateDataKey. For an SSE-KMS destination, the key policy must also allowmaintenance.s3tables.amazonaws.com, or table creation fails and planning never completes (see preflight §7 and IAM roles).
Post-migration guardrails (verify success before Complete)
Full detail in references/validation.md.
Run checks 1 to 7 on the succeeded table before Complete, because Sync is refused after Complete. After Complete, only confirm that get-table shows managedByService null.
Terminal state:
state: succeeded,failureReasonnull.Object accounting:
objectsFailedMigrationis 0 andobjectsMigratedmatches the planned object count. If it differs, check the source object count (an extra file from an earlier sync is legitimate) and confirm with row-count parity before treating it as a failure.Row count parity: destination
count(*)equals source.Partition parity: per-partition row counts match (partitioned tables).
Spot-check rows: sample values match, including converted columns such as timestamps.
Schema review: every column's type label (
Lossless,Widened,Converted) was accepted in the plan; none isIncompatibleorUnsupported.Catalog naming: query as
"s3tablescatalog/<table-bucket>"."<namespace>"."<table>".
Key API shapes
sourceType=glueTable(top level) andsource={"glueTable":{"catalogId":"<acct>","databaseName":"<db>","tableName":"<tbl>"}}.catalogIdis required. The role member isroleARN.--destination-table={"tableBucketARN":"<arn>","namespace":"<ns>","name":"<tbl>"}.migrationOutputis required:{bucket, prefixes}, wherebucketis a general-purpose bucket ARN andprefixesoptionally setstableCompatibilityReport,objectCompatibilityReport,objectMigrationReport.Optional
dataMigration.migrationPolicies:unmatchedSourceFieldsPolicy(fail|drop),incompatibleValuesPolicy(fail|setNull). Both defaultfail.Do not add
sts:ExternalIdto the trust policy; Amazon S3 Tables does not send one.States:
new → planning → pendingReview → queued → migrating → succeeded, plusfailed,cancelling,cancelled.On cancel or failure the destination table is kept. If
hasValidPlanis true, resume withsync-table-migration; otherwise delete the table and start again. Rules in references/lifecycle.md.Complete only a
succeededmigration. Completing twice returns409 ConflictExceptionor400 BadRequestException; treat either as "already done."Athena reference:
"s3tablescatalog/<table-bucket>"."<namespace>"."<table>". Requires the table bucket to be integrated with the Glue Data Catalog.
Security considerations
Instructions only; no shipped code or dependencies.
Migration role: trusts
migration.s3tables.amazonaws.comwithaws:SourceAccountandaws:SourceArnconditions; permissions scoped to the exact source, destination, and report resources.Encryption: SSE-KMS on the destination table bucket and report bucket is recommended, with TLS-only bucket policies. A customer managed destination key must allow
maintenance.s3tables.amazonaws.comin its key policy.Logging: enable CloudTrail for the migration APIs and role assumptions, S3 data-event or server-access logging on the buckets involved, and CloudWatch alarms on failures. Encrypt log destinations.
No secrets in generated commands, reports, or state files.
References
| File | Topic |
|---|---|
| references/api-setup.md | Confirming the migration commands are in the aws s3tables CLI |
| references/preflight.md | Credentials, source checks, partition locations, Lake Formation, destination |
| references/iam-roles.md | Caller policy and migration role trust and permissions |
| references/lifecycle.md | State machine and per-phase calls: start, review, approve, sync, complete |
| references/validation.md | Athena row-count, aggregate, and sample checks; catalog naming |
| references/troubleshooting.md | failureReason codes and fixes |
Files
- ATTRIBUTION.mdAdded by AutoGineer957 B
- LICENSELicence file added by AutoGineer9.9 KiB
- NOTICELicence file added by AutoGineer67 B
- SKILL.md16.1 KiB
- references/api-setup.md1.0 KiB
- references/iam-roles.md18.9 KiB
- references/lifecycle.md20.4 KiB
- references/preflight.md12.6 KiB
- references/troubleshooting.md7.8 KiB
- references/validation.md4.9 KiB
references/lifecycle.md
Migration lifecycle
Full state machine plus the per-phase request shapes and calls, in order: Start + plan →
Plan review → Approve + migrate → Sync (optional) → Complete. Pre-flight and IAM setup are in
references/preflight.md and references/iam-roles.md; failure codes are in
references/troubleshooting.md; post-migration checks are in references/validation.md.
State machine
new → planning → pendingReview → queued → migrating → succeeded ──(complete)──▶ table managedByService cleared
│
failed / cancelling → cancelledMigrationState is exactly: new, planning, pendingReview,
queued, migrating, succeeded, failed, cancelling, cancelled. There is no active
state, Complete does not transition the migration; it promotes the table, returns an empty body,
and clears the table's managedByService flag (see Complete below).
| State | Meaning | Allowed next action |
|---|---|---|
new | Migration record created, planning not yet started | Poll get-table-migration |
planning | Async plan generation in progress | Poll get-table-migration |
pendingReview | Plan ready (hasValidPlan: true); paused for review | update-table-migration --requested-state queued (approve) or cancel |
queued | Approved, waiting to be picked up | Poll; no action needed |
migrating | Copying + converting + compacting | Poll migrationStats |
succeeded | Copy finished (objectsFailedMigration 0; confirm data via row-count parity) | complete-table-migration --table-arn ... |
failed | Plan or copy failed | Read failureReason; fix; then recover (see Recovery below) |
cancelling | Cancellation in progress | Poll until cancelled |
cancelled | Cancelled | Destination table is kept. Resume with Sync if hasValidPlan is true, or delete the table and Start again (see Recovery below) |
Recovery after a cancel or fail
A failed or cancelled migration keeps the destination table and any data already committed. The
table name stays taken, so a plain re-Start returns a ConflictException (409)
until you delete the table. Check hasValidPlan (get-table-migration or
list-table-migrations --table-arn <ARN>), then:
Failed in planning, no table created:
get-tablereturnsNotFoundException. Fix the cause and start a new migration.hasValidPlanis true and the cause is fixed outside the migration settings (for example a permission you have since granted, or a cancel at review): resume withsync-table-migration. Sync plans only what is not yet migrated.The fix changes migration settings (
migrationPolicies, sort order, destination schema, or the source table): delete the destination table and Start again. Sync reuses the original source and settings, so it cannot carry these changes.hasValidPlanis false: delete the destination table and Start again.To abandon a migration, delete the destination table.
Deleting a table and starting again are changes; confirm with the user first.
Notes
Planning is asynchronous; compatibility failures appear as
failedwith afailureReason, or as invalid findings in the compatibility reports, not as an immediate CLI error.complete-table-migrationshould be called only on asucceededmigration (completing afailed/cancelledrun would publish a partial table) and takes--table-arn, not--migration-id.
Start + plan
Use --cli-input-json with a JSON body.
Request body (start-migration.json)
{
"roleARN": "arn:aws:iam::<ACCT>:role/<MIGRATION_ROLE>",
"sourceType": "glueTable",
"source": {
"glueTable": {
"catalogId": "<ACCT>",
"databaseName": "<DB>",
"tableName": "<TBL>"
}
},
"destinationTable": {
"tableBucketARN": "arn:aws:s3tables:<REGION>:<ACCT>:bucket/<DEST_BUCKET>",
"namespace": "<NS>",
"name": "<DEST_TABLE_NAME>"
},
"reviewMigrationPlan": true,
"migrationOutput": {
"bucket": "arn:aws:s3:::<REPORT_BUCKET>",
"prefixes": {
"tableCompatibilityReport": "migration-api-reports",
"objectCompatibilityReport": "migration-api-reports",
"objectMigrationReport": "migration-api-reports"
}
}
}sourceTypeis a top-level member (glueTable), andsourceholds onlyglueTable. The role member isroleARN.catalogIdis REQUIRED in the source; omitting it fails plan generation.Sort order (optional): set it as
destinationTable.icebergSortOrder. Each field refers to a destination column by field ID (sourceId). Start rejects a field that names no column or whose transform does not fit the column type. Rows are sorted within each data file the migration writes.migrationOutputis MANDATORY, and its shape is{bucket, prefixes}:bucketis one general-purpose bucket ARN (required,arn:aws:s3:::<name>);prefixesnames an optional key prefix for each of the three reports,tableCompatibilityReport,objectCompatibilityReport,objectMigrationReport(a report with no prefix is written at the bucket root). Reports are written under<prefix>/<migration-id>/<report-type>/v1/...; take each object key frommigrationOutput.<report>.keyrather than building the path, because the folder layout can change. For the object compatibility and object migration reports the key points to the report's manifest; for the table compatibility report it points to the report file. An emptymigrationOutput{}is rejected becausebucketis required. The example gives all three reports the same prefix so a singles3:PutObjectgrant on that prefix covers them (seereferences/iam-roles.md); if you give them distinct prefixes, the role needs one grant per prefix.reviewMigrationPlan: truepauses atpendingReviewso you can inspect before any data moves. Always set it; never auto-approve.Do not add an
sts:ExternalIdcondition to the role trust policy (seereferences/iam-roles.md); the migration service does not send one (there is no API field for it), so such a condition would fail every assume.
Call
aws s3tables start-table-migration \
--region <R> \
--cli-input-json file://start-migration.jsonResponse: { "migrationId": "...", "state": "..." } (new or planning). Record the migrationId.
Then
Poll with get-table-migration until state leaves new and planning. Planning is async, wait
~45–60s between polls. Expect pendingReview with hasValidPlan: true, or failed with a
named failureReason (see references/troubleshooting.md). Proceed to the Plan review section
below.
Plan review
At pendingReview, pull the full record and inspect before approving.
aws s3tables get-table-migration \
--region <R> \
--migration-id <ID>What to check
The current API does not return a plannedTransformations list on get-table-migration. The plan is reviewed through the two compatibility reports that planning writes (before any data moves), surfaced in the migrationOutput echo of the record.
hasValidPlan: trueandstate: pendingReview.migrationOutput(response shape) carriesbucketplus aReportOverviewfor each report,tableCompatibilityReport,objectCompatibilityReport, and (only after the migrating phase)objectMigrationReport. EachReportOverviewhasprefix,key,checksumCRC64NVME, andinvalidFindings(a map of counts by category:access,metadata,data,internal). A populatedkeyconfirms the report was written (so report-bucket write works); non-zeroinvalidFindingsmeans blocking issues were found.Read the table compatibility report (the CSV object at
migrationOutput.tableCompatibilityReport.key; the report is plaintext/csv, not gzipped, soaws s3 cp s3://<REPORT_BUCKET>/<key> -reads it directly and the key endsreport.csv) for the per-column type changes. Each row:finding_type(invalidblocks the migration,informationaldoes not),finding_category(access/metadata/data/internal),finding_subcategory(e.g.schemaTransformation.widened,schemaTransformation.converted,glueTableParameters), anddetails(JSON:fieldPath,glueType,destinationType, partition flag, the applicable policy, andoutcome). The type-change class is the lowercase value infinding_subcategory(schemaTransformation.<class>):lossless,widened,converted,incompatible,unsupported. Match it case-insensitively; the Title-case labels below are display only:Lossless, type and every value unchanged.
Widened, larger type of the same kind, no value lost (e.g.
int→long,varchar(n)→string). Acceptable; confirm intent.Converted, the value is preserved except where the conversion itself loses precision (e.g.
char(n)→stringdrops length/padding; Parquet INT96 nanoseconds → Icebergtimestamptzmicroseconds truncates sub-microsecond values). Review and accept consciously; this is not lossless.Incompatible, the destination type can't hold every value (e.g.
bigint→int).incompatibleValuesPolicydecides column-wide, not per object:fail(default) fails the run,setNullwrites the entire column null (the whole column, whatever the values). Treat as data loss requiring explicit user sign-off.Unsupported, no Iceberg type / unsupported change; the migration fails unless you drop the column via
unmatchedSourceFieldsPolicy: drop(removes the whole column; data loss requiring sign-off; you cannot drop a partition column).
Object compatibility report (
objectCompatibilityReport.key) lists each scanned source object and any issue that keeps it out of the migration.planningStats,objectsScanned,objectsPlanned,partitionCount.migrationStats.objectsPendingMigrationshould match the source object count you expect.
Compact plan view
aws s3tables get-table-migration \
--region <R> --migration-id <ID> \
--query '{state:state,hasValidPlan:hasValidPlan,reportKey:migrationOutput.tableCompatibilityReport.key,invalid:migrationOutput.tableCompatibilityReport.invalidFindings,objInvalid:migrationOutput.objectCompatibilityReport.invalidFindings,pending:migrationStats.objectsPendingMigration}'Decision
hasValidPlan: true, both compatibility reports'invalidFindingsall zero, and the table compatibility report shows only type changes you accept (Lossless/Widened, plus anyConvertedyou consciously accept) → safe to approve (see Approve + migrate below).Before approving, scan the table compatibility report's
details.outcome: anysetNullordroppedentry is column-wide data loss (the whole column, not a per-object issue) and needs explicit user sign-off. Plan review does not block on it, so you must check.Any non-zero
invalidFindings, anyinvalidfinding_type, anUnsupportedchange, or anIncompatiblechange you cannot accept → cancel this migration first withupdate-table-migration --requested-state cancelled. A cancelled migration keeps its destination table. To changemigrationPolicies, the destination schema, or the sort order, delete the destination table (with the user's confirmation) and Start again; Sync reuses the Start's settings, so it cannot apply these changes. If you only fixed the source andhasValidPlanis true, resume withsync-table-migrationinstead. See Recovery after a cancel or fail.If
hasValidPlanis nottrue, ormigrationOutput.tableCompatibilityReport.keyis absent / the report cannot be read, treat it as cannot-verify and STOP. Never read a missing report as "no findings / clean". Re-pull the record; if it stays absent, open a case with AWS Support (include themigrationIdand region) before approving.
Approve + migrate
Only after the plan is reviewed and the user has approved it (see "Before you migrate" in
SKILL.md). Approving starts the data copy into the destination table.
Approve (release into the copy)
--requested-state MUST be lowercase. The enum accepts only queued and cancelled.
QUEUED (or any other casing) fails with ValidationException: Member must satisfy enum value set: [cancelled, queued]. Always pass queued, never QUEUED.
aws s3tables update-table-migration \
--region <R> \
--migration-id <ID> \
--requested-state queuedResponse shows state: queued.
Poll to completion
aws s3tables get-table-migration \
--region <R> --migration-id <ID> \
--query '{state:state,objectsMigrated:migrationStats.objectsMigrated,objectsPending:migrationStats.objectsPendingMigration,failed:migrationStats.objectsFailedMigration,failureReason:failureReason}'queued → migrating → succeeded. Wait ~30–60s between polls. Do not poll indefinitely: if the state has not changed for roughly 30 minutes on a modest table, or elapsed time is well past what a plain S3 copy of the same byte volume would take, stop and treat it as the stuck-in-migratingcase under Failure handling below.Success:
state: succeeded,objectsPending: 0,objectsFailedMigration: 0,objectsMigratedequal toplanningStats.objectsPlanned,failureReason: null. Then confirm the data with row-count parity (references/validation.md).If
objectsMigrateddiffers from the planned count, check the source object count before calling it a bug; a table with an extra file from an earlier sync legitimately reports more.
Failure handling
Copy is all-or-nothing. If even one object fails to copy, the whole migration and table go to
failedwith afailureReason(oftenobjectMigrationReport); the run never endssucceededwith failed objects. The destination table is kept. Read the object migration report for the failed key anderror_code, fix the cause, then resume withsync-table-migrationifhasValidPlanis true, or delete the table and Start again.failedwith afailureReason→ look it up inreferences/troubleshooting.md, fix, then recover per the Recovery rules above.Stuck in
queuedwith no pickup for an extended period, nofailureReason→ likely a service-side delay before the job starts, not your setup. Capture themigrationIdand open a case with AWS Support (include themigrationIdand region).Stuck in
migratingwith no progress (as a rough basis: no state change for ~30 minutes on a modest table, or elapsed time well beyond a plain S3 copy of the same byte volume), and nofailureReason→ capture themigrationIdand open a case with AWS Support (include themigrationIdand region). A prolongedmigratingwith no progress is a failure signal, not success.
Sync (optional)
Use Sync to catch the destination up with changes the source accumulated since the plan, before you cut over. Repeat until the delta is current.
Request body (sync-migration.json)
{
"roleARN": "arn:aws:iam::<ACCT>:role/<MIGRATION_ROLE>",
"tableARN": "<DESTINATION_TABLE_ARN>",
"reviewMigrationPlan": true,
"migrationOutput": {
"bucket": "arn:aws:s3:::<REPORT_BUCKET>",
"prefixes": {
"tableCompatibilityReport": "migration-api-reports",
"objectCompatibilityReport": "migration-api-reports",
"objectMigrationReport": "migration-api-reports"
}
}
}Reuse the Start report prefix so the role's existing WriteReports grant covers it. A different
prefix needs its own s3:PutObject grant, or Sync fails with destinationPermissions.
aws s3tables sync-table-migration \
--region <R> \
--cli-input-json file://sync-migration.jsonThe Sync body is NOT the same as Start: it takes only roleARN, tableARN (the
migrated table, top level), reviewMigrationPlan, and migrationOutput, there is no source. The
service infers the source from the original migration and uses the last valid plan as the diff
basis. (A reviewMigrationPlan: true sync plans the delta and pauses at
pendingReview, same as Start.)
Semantics
Sync applies only the delta since the last plan or sync, not a full re-copy. A Sync is a change to the destination table; it needs the same plan review and approval as Start.
Sync reconciles new and appended source objects. It does not handle an already-migrated object that was overwritten in place (the same S3 key rewritten): the sync fails with failure code
objectCompatibilityReportand findingdata.objectModified. HiveINSERT OVERWRITErewrites the same keys, so a source changed that way between plan and sync cannot be synced incrementally, re-migrate it fresh instead.Each Sync returns its own
migrationIdandstate; poll that id, not the original migration's.With
reviewMigrationPlan: truethe sync parks atpendingReview; approve it withupdate-table-migration --requested-state queuedon the sync's ownmigrationId, the same as Start. If you copyreviewMigrationPlan: trueand never approve, the sync sits inpendingReviewand the next Sync is rejected.A Sync started while anything on the table is still non-terminal returns
ConflictException(409); a Sync after Complete returnsBadRequestException(400).A Sync with no source changes succeeds with 0 objects planned.
On cancel or fail the destination table is kept; the last valid plan is the basis for the next Sync.
Repeat Sync until
objectsPendingMigrationis 0 and the source is quiesced, then Complete.
When to Sync vs. Complete
Source still receiving writes and you want minimal cutover delta → Sync repeatedly, then Complete during a short freeze.
Source already frozen / point-in-time is fine → skip Sync, go straight to Complete.
Complete
Promotes the migrated destination table to an ordinary read-write S3 Table. Complete does not set
a migration state called active (there is none) and returns an empty body; confirm it by
calling get-table and checking that the table's managedByService is cleared to null.
aws s3tables complete-table-migration \
--region <R> \
--table-arn <DESTINATION_TABLE_ARN>Important
Takes
--table-arn, NOT--migration-id. The table ARN is in the migration record (get-table-migration→tableARN).Complete only a
succeededmigration. Completing promotes whatever data is committed on the destination table to read-write, so completing afailedorcancelledmigration would publish a partial table. Calling Complete while the copy is still in progress (queued/migrating) returnsConflictException(409, e.g.MIGRATION_IN_PROGRESS); a malformed or otherwise invalid request returnsBadRequestException(400).Completing a migration that is already complete returns
ConflictException(409) orBadRequestException(400). Treat either as "already done", not an error to fix: confirmget-tableshowsmanagedByServicenull, then stop.Complete is one-way, so validate first. After Complete, Sync is refused (
BadRequestException400), so you cannot reconcile a shortfall afterward. The destination holds committed data and is queryable while stillsucceeded(before Complete), so run the destination row-count parity check (references/validation.md) on thesucceededtable before Complete; if it is short,sync-table-migrationto reconcile (only possible pre-Complete), and Complete only once parity holds.After Complete, confirm
get-tableshowsmanagedByServicenull. The table is then read-write and queryable normally.
Response
CompleteTableMigration returns an empty body (HTTP success with no content) and does not
change the migration's state. There is no active state and no completedAt field. Confirm
completion by calling get-table --table-arn <ARN> and checking that managedByService is cleared
to null (it reads migration.s3tables.amazonaws.com while the migration owns the table and is
absent/null once Complete hands it over), then query the table (validation).
List migrations
ListTableMigrations discovers migrations on the endpoint. The request members are
tableBucketARN, namespace, name, tableARN, state, hasValidPlan, maxResults,
and nextToken. It is not account-wide: scope it by tableBucketARN (optionally narrowed by namespace, then name), or by
tableARN alone. A bare call is rejected.
# in-flight migrations for one destination table bucket
aws s3tables list-table-migrations \
--region <R> \
--table-bucket-arn <DEST_BUCKET_ARN> \
--state planning
# migrations for one destination table
aws s3tables list-table-migrations \
--region <R> \
--table-arn <DESTINATION_TABLE_ARN>Requirements and compatibility
Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta
“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.
Needs: Needs an MCP server
Security
No issues found by our static scan· No issues found
A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.
Attribution and licence
- Published by
- AWS
- Publisher
- AWS (Official)
- Source
- aws/agent-toolkit-for-aws · skills/specialized-skills/analytics-skills/migrating-hive-tables-to-s3-tables · commit 0a218f8
- Licence
- Apache License 2.0 (redistribution allowed)
- LICENSE (added by AutoGineer from LICENSE)
- NOTICE (added by AutoGineer from NOTICE)
- ATTRIBUTION.md (written by AutoGineer)
- Synced
AutoGineer is not affiliated with or endorsed by AWS.
Download ZIP
The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download migrating-hive-tables-to-s3-tables.zip