FAILURE MAP
← Case archive

FA-11586 / Filesystem semantics / Open access

A dangling symbolic link disappears from metadata results · case 01

A dangling symbolic link disappears from metadata results.

Verified by executionVariant 1 · 6 checks per implementationDownload source bundle ↓JSON ↗

ROOT CAUSE

Entry inspection follows a symbolic link before reporting the entry type.

VERIFIED REPAIR

Report the named entry itself for lstat-style inspection.

Unsuccessful approach: Falling back to link type only for missing targets still misreports links to existing files.

Case contract

Deterministic in-memory filesystem model. entries maps flat names to [kind,target], where kind is file,dir,or link and target is used only for links. Return named entry kind or missing without following any link.

Why this case matters

An offline semantic experiment for file API clients; it models only the stated operations, not a complete operating system.

1 / The failure

Exit 1
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(entries, name):
    entry=entries.get(name)
    if entry is None: return 'missing'
    if entry[0]=='link': return entries.get(entry[1],['missing'])[0]
    return entry[0]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
name='link'+str(N)
check('dangling link exists',solve({name:['link','absent']},name),'link')
check('link to file remains link',solve({name:['link','f'],'f':['file',None]},name),'link')
check('link to directory remains link',solve({name:['link','d'],'d':['dir',None]},name),'link')
check('ordinary file',solve({'f':['file',None]},'f'),'file')
check('missing entry',solve({},name),'missing')
check('self link does not recurse',solve({name:['link',name]},name),'link')
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
Boundary fixtureActualExpectedOutcome
dangling link existsmissinglinkFailed
link to file remains linkfilelinkFailed
link to directory remains linkdirlinkFailed
ordinary filefilefilePassed
missing entrymissingmissingPassed
self link does not recurselinklinkPassed

SHA-256 / a1ef757f183702776dbaf0cabff756246b7167a47ad5f09912f5e02a5120fe61

2 / The unsuccessful fix

Exit 1
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(entries, name):
    entry=entries.get(name)
    if entry is None: return 'missing'
    if entry[0]=='link': return entries.get(entry[1],['link'])[0]
    return entry[0]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
name='link'+str(N)
check('dangling link exists',solve({name:['link','absent']},name),'link')
check('link to file remains link',solve({name:['link','f'],'f':['file',None]},name),'link')
check('link to directory remains link',solve({name:['link','d'],'d':['dir',None]},name),'link')
check('ordinary file',solve({'f':['file',None]},'f'),'file')
check('missing entry',solve({},name),'missing')
check('self link does not recurse',solve({name:['link',name]},name),'link')
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
Boundary fixtureActualExpectedOutcome
dangling link existslinklinkPassed
link to file remains linkfilelinkFailed
link to directory remains linkdirlinkFailed
ordinary filefilefilePassed
missing entrymissingmissingPassed
self link does not recurselinklinkPassed

SHA-256 / db377e0d2869f9e0377514b87cbc80fe62c2c6c7e56ea5b33d3e290871624341

3 / The verified repair

Exit 0
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(entries, name):
    return entries.get(name,['missing'])[0]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
name='link'+str(N)
check('dangling link exists',solve({name:['link','absent']},name),'link')
check('link to file remains link',solve({name:['link','f'],'f':['file',None]},name),'link')
check('link to directory remains link',solve({name:['link','d'],'d':['dir',None]},name),'link')
check('ordinary file',solve({'f':['file',None]},'f'),'file')
check('missing entry',solve({},name),'missing')
check('self link does not recurse',solve({name:['link',name]},name),'link')
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
Boundary fixtureActualExpectedOutcome
dangling link existslinklinkPassed
link to file remains linklinklinkPassed
link to directory remains linklinklinkPassed
ordinary filefilefilePassed
missing entrymissingmissingPassed
self link does not recurselinklinkPassed

SHA-256 / 7d16a7f59866273478f2517d07da0bc1a85acb1eaa62b11fc99e8daa2a25fddd

Verification & scope

This reproducer isolates one failure mechanism. Results cover the supplied fixtures. Variants within a family share a test contract and should remain grouped when constructing evaluation splits. Related mechanisms with a shared evaluation_group must also remain together; these controlled models are not independent production incidents.

Observations recorded using Python 3.12.14 at 2026-09-29T14:38:49.248546+00:00.

Case digest / 29e4956cb249e6e5806bbbde1e28c6b2ca9187c30c4f15f1ec56b2ea8ba7834a