canonical / canonical/cloud-init

error handling issue: cloud-init produces a traceback on invalid user configuration

Open
#7,006 2 comments 0 reactions 0 assignees View on GitHub
bug new
Dominant language
Python
Stars
3.8k
Forks
1.1k
Avg merge
2d 23h
Merged PRs (30d)
18

Description

# Bug report

`_maybe_set_hostname()` in `cloudinit/cmd/main.py` calls `util.get_hostname_fqdn(init.cfg, cloud, metadata_only=True)` before entering its only `try`/`except` block (that block guards just the later `cc_set_hostname.handle(...)` call). `get_hostname_fqdn()` in `cloudinit/util.py` then does `if "hostname" in cfg and cfg["hostname"].find(".") > 0:` with no type check or `str()` cast on `cfg["hostname"]`. If the merged config's hostname value is ever a non-string (e.g. an all-digit value that gets interpreted as an int somewhere upstream instead of staying a string), this raises `AttributeError: 'int' object has no attribute 'find'` — and because the call happens outside the guarded block, the exception propagates uncaught out of `main_init`, killing the entire `init-local` stage.

On a NoCloud/local-datasource boot (e.g. Raspberry Pi OS via Raspberry Pi Imager), this cascades to fail `cloud-init-local.service`, `cloud-init-network.service`, `cloud-config.service`, and `cloud-final.service` on every subsequent boot — `write_files`, `runcmd`, and (critically) network-config rendering never run again until the underlying config is fixed. On a headless device this silently and permanently breaks network provisioning, with no user-facing error beyond the log file.

## Steps to reproduce the problem

1. Set a purely-numeric string as the hostname in `user-data`:

#cloud-config
hostname: "123456"

2. Boot with this cloud-config via the NoCloud datasource (`dsmode: local`).
3. Inspect `/var/log/cloud-init.log` — `init-local` fails with the traceback below, and `cloud-init status --long` reports failure for all stages.

Reproduced specifically with Raspberry Pi Imager's cloud-init-based headless setup (`user-data` + `meta-data` + `network-config` seeded via `file:///boot/firmware`), but the crash is in generic `cloudinit/util.py` and `cloudinit/cmd/main.py` code, not anything Raspberry Pi OS-specific — it should reproduce identically on any NoCloud (or other datasource) boot where a numeric hostname reaches `get_hostname_fqdn()`.

## Environment details

- Cloud-init version: 25.2
- Operating System Distribution: Raspberry Pi OS (Debian 13 "Bookworm" based), aarch64
- Cloud provider, platform or installer type: Raspberry Pi Imager (NoCloud datasource, `dsmode: local`, seeded from `file:///boot/firmware`)

## cloud-init logs

2026-08-14 18:26:13,103 - main.py[ERROR]: failed stage init-local
Traceback (most recent call last):
File "/usr/lib/python3/dist-packages/cloudinit/cmd/main.py", line 963, in status_wrapper
ret = functor(name, args)
File "/usr/lib/python3/dist-packages/cloudinit/cmd/main.py", line 591, in main_init
_maybe_set_hostname(init, stage="local", retry_stage="network")
File "/usr/lib/python3/dist-packages/cloudinit/cmd/main.py", line 1046, in _maybe_set_hostname
(hostname, _fqdn, _) = util.get_hostname_fqdn(init.cfg, cloud, metadata_only=True)
File "/usr/lib/python3/dist-packages/cloudinit/util.py", line 1224, in get_hostname_fqdn
if "hostname" in cfg and cfg["hostname"].find(".") > 0:
AttributeError: 'int' object has no attribute 'find'
failed run of stage init-local
------------------------------------------------------------
systemd[1]: cloud-init-local.service: Main process exited, code=exited, status=1/FAILURE
systemd[1]: cloud-init-local.service: Failed with result 'exit-code'.
systemd[1]: Failed to start cloud-init-local.service - Cloud-init: Local Stage (pre-network).
systemd[1]: cloud-init-network.service: Main process exited, code=exited, status=1/FAILURE
systemd[1]: cloud-init-network.service: Failed with result 'exit-code'.
systemd[1]: Failed to start cloud-init-network.service - Cloud-init: Network Stage.
systemd[1]: Failed to start cloud-config.service - Cloud-init: Config Stage.
systemd[1]: Failed to start cloud-final.service - Cloud-init: Final Stage.

Same traceback recurs identically on every boot from the point a numeric hostname is first consumed onward (confirmed across 20+ boots spanning several days on the reporter's device); the very first boot instead hit the same underlying `AttributeError`, but through the module-pipeline call path (`cc_set_hostname` under `modules.py`), where it is caught and logged only as a `WARNING` rather than aborting the stage:

handlers.py[DEBUG]: finish: init-local/config-set_hostname: FAIL: running config-set_hostname with frequency once-per-instance
log_util.py[WARNING]: Running module set_hostname () failed

Suggested fix: either `str(cfg["hostname"]).find(".")` (and equivalent casts elsewhere in `get_hostname_fqdn`) in `cloudinit/util.py`, or move the `util.get_hostname_fqdn(...)` call in `_maybe_set_hostname` inside the existing `try`/`except` block so it fails as gracefully as the `cc_set_hostname.handle(...)` call right below it.

Contributor guide

Open the contributing guide

Research direction

Start with get_hostname_fqdn in cloudinit/util.py and _maybe_set_hostname in cloudinit/cmd/main.py, following the traceback and existing try/except boundary. Reproduce with the NoCloud configuration described in the issue, then verify that a numeric hostname no longer produces an uncaught traceback or prevents later cloud-init stages from running.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
infrastructure
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
75/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.