Skip to content

fix(resolver): treat non-mapping manifest service as structure error - #5814

Closed
0xacee wants to merge 1 commit into
Osmantic:public-betafrom
0xacee:fix/resolver-service-mapping
Closed

0xacee wants to merge 1 commit into
Osmantic:public-betafrom
0xacee:fix/resolver-service-mapping

Conversation

@0xacee

@0xacee 0xacee commented Sep 18, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes #5712.

resolve-compose-stack.sh crashed with an uncaught AttributeError when a manifest's service key was not a mapping (e.g. service: not-a-mapping or service: 42), in both the bundled-extension and user-extension loops:

  • manifest.get("service", {}) returns the scalar, so service.get("gpu_backends", ...) raises AttributeError
  • AttributeError is not covered by the structure-error handler (KeyError, TypeError), so it fell through to raise — a full Python traceback and exit 1 even with --skip-broken

The fix validates isinstance(service, dict) immediately after reading the key in both loops, raising TypeError with a descriptive message. That routes through the existing structure-error path: ERROR: Failed to parse manifest … is printed, and --skip-broken skips the extension as intended.

Reproduction on public-beta (both loops):

Traceback (most recent call last):
  File "<stdin>", line 510, in <module>
AttributeError: 'str' object has no attribute 'get'

Validation

  • New section 27 in tests/test-resolve-compose-resilient.sh covers a scalar service in both the bundled-extension and user-extension loops: --skip-broken must exit 0, print the structure-error message, and produce no traceback
  • Full suite: 38 passed, 0 failed (Docker Compose render check skipped — no daemon in this environment)
  • bash -n clean; ShellCheck reports no new findings on changed lines

A manifest whose service key is a scalar raised AttributeError in both
extension loops, which fell outside the structure-error handler and
re-raised — crashing resolve-compose-stack.sh even with --skip-broken.
Validate the service mapping explicitly so the existing parse-error
path reports it and honors --skip-broken.
@roshangupta00750

Copy link
Copy Markdown
Contributor

I tested this against a manifest whose service: is a string (service: oops) in data/user-extensions/zz-bad/:

$ bash scripts/resolve-compose-stack.sh --script-dir "$INSTALL" --tier 1 --gpu-backend cpu
ERROR: Failed to parse manifest for zz-bad: manifest 'service' must be a mapping, got str
  Manifest path: .../data/user-extensions/zz-bad/manifest.yaml
  This service will be skipped. Fix the manifest or disable the service.
$ echo $?
1

stdout is empty, so no compose flags come out for any service. Every ods command resolves the stack through this script, so a single malformed manifest still stops the whole stack, while the message says only that one service is skipped. It's the same for a list or an empty service:, in both extensions/services/ and data/user-extensions/. Skipping the manifest with a warning and continuing, instead of raising, keeps the rest of the stack resolvable. #5713, #5841 and #5953 take that approach.

@roshangupta00750

Copy link
Copy Markdown
Contributor

Correction to my comment above: I tested without --skip-broken. On main, a manifest with a YAML syntax error also prints "This service will be skipped" and exits 1 unless --skip-broken is passed, so this PR follows the resolver's existing structure-error handling rather than introducing new behaviour. Apologies for the noise.

@0xacee 0xacee closed this Oct 4, 2026
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.

2 participants