Skip to content

fix(core): read legacy key=value metadata in .json session files#1738

Open
dashitongzhi wants to merge 2 commits into
ComposioHQ:mainfrom
dashitongzhi:fix/legacy-keyvalue-metadata-in-json
Open

fix(core): read legacy key=value metadata in .json session files#1738
dashitongzhi wants to merge 2 commits into
ComposioHQ:mainfrom
dashitongzhi:fix/legacy-keyvalue-metadata-in-json

Conversation

@dashitongzhi
Copy link
Copy Markdown

Problem

ao start fails when a valid canonical orchestrator session is alive but its .json metadata file contains legacy key=value format instead of JSON. parseMetadataContent() in metadata.ts only attempts JSON.parse() and returns null on non-JSON content, while reserveSessionId() sees the file as existing — creating a self-contradictory state that blocks recovery.

Fixes #1720

Fix

Modified parseMetadataContent() to fall back to parseKeyValueContent() (an existing helper used elsewhere in the codebase for legacy formats) when:

  1. JSON.parse() fails, AND
  2. The content does NOT start with { or [ (preserving corrupt-JSON behavior)

This is the smallest change that resolves the issue — only packages/core/src/metadata.ts is modified (plus tests).

Key design choices

  • JSON-first: valid JSON is always preferred; the fallback only triggers on parse failure
  • Corrupt JSON preserved: content starting with { or [ that fails to parse is still treated as corrupt (not silently parsed as key=value)
  • Empty content returns null: an empty file still returns null (matches existing reserveSessionId sentinel)
  • No destructive behavior: legacy metadata files are read, not deleted — a subsequent writeMetadata/updateMetadata call will rewrite in JSON format

Tests added

4 new tests in packages/core/src/__tests__/metadata.test.ts:

  • readMetadata parses key=value content from a .json file (with runtimeHandle, tmuxName, status)
  • readMetadataRaw parses key=value content from a .json file
  • Returns null for empty key=value file
  • Returns null for corrupt JSON that starts with {

Validation

  • pnpm --filter @aoagents/ao-core test -- metadata — 47/47 passed
  • pnpm --filter @aoagents/ao-core typecheck — clean

parseMetadataContent() previously only attempted JSON.parse() and returned
null on any failure. When a .json file contains legacy key=value metadata
(as seen in pre-V2 canonical orchestrator sessions), readMetadata() and
readMetadataRaw() treated it as missing, causing ensureOrchestrator() to
fail with a self-contradictory 'session already exists' error.

Fix: fall back to parseKeyValueContent() when JSON.parse fails and the
content does not start with '{' or '['. Content that looks like JSON but
fails to parse is still treated as corrupt.

Closes ComposioHQ#1720
Copilot AI review requested due to automatic review settings May 8, 2026 15:08
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes a recovery bug where ao start can’t reuse an existing canonical orchestrator if its session metadata file has a .json name but contains legacy key=value content. It updates core metadata parsing to be JSON-first while supporting the legacy format as a fallback, and adds unit tests covering the new behavior.

Changes:

  • Extend parseMetadataContent() to fall back to legacy key=value parsing when JSON parsing fails and the content doesn’t look like intended JSON ({ or [).
  • Preserve existing “corrupt JSON” behavior by continuing to return null for invalid content that starts like JSON.
  • Add metadata unit tests covering readMetadata/readMetadataRaw for legacy content in .json files.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
packages/core/src/metadata.ts Adds legacy key=value fallback parsing to session metadata reads while preserving corrupt-JSON handling.
packages/core/src/__tests__/metadata.test.ts Adds unit tests proving legacy key=value content inside .json files is readable and corrupt/empty cases still return null.

Comment thread packages/core/src/metadata.ts Outdated
/** Parse JSON metadata file content. Returns null on invalid JSON. */
/** Parse metadata file content. Returns null on invalid content.
* Supports JSON format (current) and legacy key=value format.
* If content starts with '{' but fails JSON parse, it's corrupt — return null.
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed — updated the doc comment to mention both { and [ in the corrupt-JSON check.

expect(meta!.runtimeHandle?.runtimeName).toBe("tmux");
expect(meta!.status).toBe("working");
});

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Acknowledged — the session-manager regression test for ensureOrchestrator recovery would require deeper mocking of the spawn/reservation path. The existing unit tests now cover the core parsing and round-trip behavior. A higher-level integration test can be added in a follow-up.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented May 8, 2026

Greptile Summary

Fixes ao start blocking when a session metadata file uses the legacy line-oriented format instead of JSON, by adding a parsing fallback in parseMetadataContent().

Confidence Score: 5/5

Safe to merge — the change is minimal, well-tested, and the fallback path is only reached for files that already failed JSON parsing.

The modification is narrowly scoped to a single private helper function. All callers pre-trim content before passing it in, and the round-trip through flattenToStringRecord and unflattenFromStringRecord correctly handles JSON-stringified nested fields.

No files require special attention.

Important Files Changed

Filename Overview
packages/core/src/metadata.ts Adds legacy line-oriented fallback in parseMetadataContent(); logic is correct and the round-trip handles JSON-stringified field values correctly.
packages/core/src/tests/metadata.test.ts Six new tests covering readMetadata, readMetadataRaw, blank/comment-only content, mutateMetadata round-trip, and the corrupt-JSON guard.

Reviews (2): Last reviewed commit: "fix: address bot review feedback on lega..." | Re-trigger Greptile

Comment thread packages/core/src/__tests__/metadata.test.ts
Comment thread packages/core/src/__tests__/metadata.test.ts
@dashitongzhi
Copy link
Copy Markdown
Author

Hi! I want to acknowledge the 4 inline issues flagged by Copilot and Greptile reviewers before requesting human review. I'll address these feedback items and push an update shortly.

Friendly ping for review once the fixes are in 🙏 CI is passing on the current version. Thank you!

- Update doc comment to mention '[' alongside '{' in corrupt-JSON check
- Add test for comment-only/blank key=value file exercising fallback path
- Add mutateMetadata round-trip test for legacy format content
@dashitongzhi
Copy link
Copy Markdown
Author

Hi! 👋 This PR looks ready for review. Could a maintainer please take a look when they have a chance? Thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(core): read legacy key=value metadata in .json session files

2 participants